SwiftUI中WebKit的callAsyncJavaScript不等待async/await函数求助
问题解决步骤
一、Swift代码调整
如果你的iOS版本在15及以上,优先使用callAsyncJavaScript的异步版本(async/await),自然等待JS异步操作完成:
// 假设你有WKWebView实例webView Task { do { let result = try await webView.callAsyncJavaScript("getPassFromIndexedDB()", in: nil, in: .defaultClient) // 更新UI必须回到主线程 if let pass = result as? String { DispatchQueue.main.async { self.pass = pass // self.pass是你绑定到UI的变量 } } } catch { print("JS调用出错: \(error)") } }
如果是iOS 14及以下,用completion handler处理异步结果:
webView.callAsyncJavaScript("getPassFromIndexedDB()", in: nil, in: .defaultClient) { result, error in if let error = error { print("错误: \(error)") return } if let pass = result as? String { DispatchQueue.main.async { self.pass = pass } } }
核心要求:JS端函数必须返回一个能resolve结果的Promise,否则callAsyncJavaScript会直接返回undefined。
二、JavaScript代码调整(sample.js)
IndexedDB操作全是异步流程,你的JS函数必须正确返回Promise,确保所有异步步骤完成后传递结果:
async function getPassFromIndexedDB() { return new Promise((resolve, reject) => { // 打开数据库 const request = indexedDB.open('MyDatabase', 1); request.onerror = () => reject('数据库打开失败'); request.onsuccess = (event) => { const db = event.target.result; // 开启只读事务 const transaction = db.transaction('passwords', 'readonly'); const store = transaction.objectStore('passwords'); // 查询数据 const getRequest = store.get('userPass'); getRequest.onsuccess = () => { // 拿到结果后resolve给Swift const pass = getRequest.result?.value || '无密码'; resolve(pass); db.close(); }; getRequest.onerror = () => { reject('获取密码失败'); db.close(); }; }; // 数据库版本升级时创建表(首次打开时触发) request.onupgradeneeded = (event) => { const db = event.target.result; if (!db.objectStoreNames.contains('passwords')) { db.createObjectStore('passwords', { keyPath: 'id' }); } }; }); }
也可以用async/await简化写法(本质还是返回Promise):
async function getPassFromIndexedDB() { const db = await openDatabase(); try { const transaction = db.transaction('passwords', 'readonly'); const store = transaction.objectStore('passwords'); // 把IndexedDB的请求包装成Promise const result = await new Promise((resolve, reject) => { const request = store.get('userPass'); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); return result?.value || '无密码'; } finally { db.close(); } } // 单独封装数据库打开逻辑 function openDatabase() { return new Promise((resolve, reject) => { const request = indexedDB.open('MyDatabase', 1); request.onerror = () => reject(request.error); request.onsuccess = (event) => resolve(event.target.result); request.onupgradeneeded = (event) => { const db = event.target.result; if (!db.objectStoreNames.contains('passwords')) { db.createObjectStore('passwords', { keyPath: 'id' }); } }; }); }
三、HTML文件(abcd.html)注意事项
确保JS代码在页面加载完成后可调用,直接在head引入脚本即可:
<!DOCTYPE html> <html> <head> <script src="sample.js"></script> </head> <body> <!-- 页面内容 --> </body> </html>
关键要点总结
- Swift端:必须用异步方式处理
callAsyncJavaScript的结果,不能同步等待,因为JS操作是异步的。 - JS端:操作IndexedDB的函数必须返回Promise,异步操作完成后用
resolve传递结果,错误时用reject抛出。 - 必须等WebView完全加载HTML页面后,再调用JS函数,否则会因找不到函数报错。
内容的提问来源于stack exchange,提问作者Elder Blood
相关产品推荐
相关产品推荐

