如何让Swift异步函数暂停直至JavaScriptCore回调返回结果
Swift 桥接 JavaScriptCore Promise 异步调用方案
核心实现基于Swift Concurrency提供的withCheckedThrowingContinuation,桥接JS Promise的resolve/reject回调,实现和原生async/await完全一致的调用体验,适配Vapor的异步路由逻辑。
核心实现逻辑
JS异步函数返回的Promise对象本身支持挂载成功/失败回调,你只需要把Swift侧Continuation的恢复方法封装成JS可识别的block,传入Promise的then/catch回调即可:
- Promise被resolve时,触发成功回调,将结果传回Swift恢复异步执行
- Promise被reject时,触发失败回调,将错误抛回Swift异步流程
可复用封装代码
首先定义通用的JS异步调用桥接方法:
import JavaScriptCore enum JSPromiseBridgeError: Error { case invalidFunctionReturn case runtimeError(String) } /// 桥接JS异步函数,支持直接await获取JS Promise执行结果 /// - Parameters: /// - jsFunction: JS侧的异步函数对象 /// - arguments: 调用JS函数需要传入的参数列表 /// - Returns: JS Promise resolve后返回的结果值 func callJSAsync(_ jsFunction: JSValue, arguments: [Any] = []) async throws -> JSValue { // 调用JS函数,拿到返回的Promise实例 guard let promise = jsFunction.call(withArguments: arguments), promise.isObject else { throw JSPromiseBridgeError.invalidFunctionReturn } return try await withCheckedThrowingContinuation { continuation in // 封装成功回调:Promise resolve时触发 let onFulfilled: @convention(block) (JSValue) -> Void = { result in continuation.resume(returning: result) } // 封装失败回调:Promise reject时触发 let onRejected: @convention(block) (JSValue) -> Void = { error in let message = error.toString() ?? "未知JS运行时错误" continuation.resume(throwing: JSPromiseBridgeError.runtimeError(message)) } // 给Promise挂载回调,注意block需要通过unsafeBitCast转换为JS可识别的对象 promise.invokeMethod("then", withArguments: [unsafeBitCast(onFulfilled, to: AnyObject.self)])? .invokeMethod("catch", withArguments: [unsafeBitCast(onRejected, to: AnyObject.self)]) } }
业务侧使用示例
替换你原来的myAsyncFunction实现即可,不需要修改Vapor侧的路由注册逻辑:
func myAsyncFunction() async -> Bool { let jsContext = JSEngine().evaluateScript(myJSCode) guard let targetJSFunc = jsContext.objectForKeyedSubscript("myAsyncJSFunction") else { return false } do { let jsResult = try await callJSAsync(targetJSFunc, arguments: [data]) // 根据业务需要把JSValue转为对应类型,这里示例转Bool return jsResult.toBool() } catch { // 按需处理错误,例如打印日志、返回默认值 print("JS异步执行失败: \(error)") return false } }
注意事项
- 所有传入JSContext的Swift闭包必须添加
@convention(block)标记,否则JavaScriptCore无法正确识别调用,会出现回调不触发、内存崩溃问题 - 确保Continuation的
resume方法只会被调用一次,上述实现中Promise的then和catch天然互斥,不会出现多次resume的问题 - 如果你的JS逻辑采用回调风格而非返回Promise,只需要把封装好的
onFulfilled、onRejected作为参数直接传给JS函数即可,桥接逻辑完全一致 - 该实现完全兼容Vapor的异步事件循环模型,等待JS执行的过程中不会阻塞服务端其他请求处理
内容的提问来源于stack exchange,提问作者mrtksn
相关产品推荐
相关产品推荐

