You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 09:25:31