首页 > 解决方案 > 如何使用 JSObjectMakeDeferredPromise 从 Swift 调用 JavaScript 中返回承诺的函数

问题描述

我希望能够从返回承诺的 JSContext 调用函数。似乎 JSObjectMakeDeferredPromise 是执行此操作的机制,但文档太少,我不确定如何执行此操作。

谁能提供一个在返回承诺的 JSContext 中调用函数的示例?

标签: iosswiftjavascriptcore

解决方案


WebKit 是一个开源项目。我发现了与该功能相关的问题。这个函数更喜欢为未处理的 Promise 拒绝抛出异常。

所以你不能使用那个函数来返回一个承诺。但是JavaScriptCore 支持 Promise 关键字。我使用 Xcode 11.4.1 在 macOS 上运行此脚本。效果很好。

const promiseA = new Promise( (resolve, reject) => {
    emulator.downloadWithHandler( (response) => {
        var isFinish = response["isFinish"];
        var isCancelled = response["isCancelled"];
        var date = response["date"];

        console.log("isFinish: " + isFinish);
        console.log("isCancelled: " + isCancelled);
        console.log("date: " + date);

        if (isFinish || isCancelled) {
            resolve(date);
        }
    });
});

emulator.downloadWithHandler是一个本机回调,将在某个时候在后台 DispatchQueue 上返回。如果您想使用该承诺并保留它。调用context.objectForKeyedSubscript("promiseA")并包装它使用JSManagedValue.


为 a 保留参考并没有多大用处Promise。更重要的是如何访问JSContext.

你可以调用 JSContext 的evaluateScript(_:). 如果您在脚本返回后保留上下文,则该上下文不会取消初始化并且可以使用。从上下文中获取对象,然后使用 JSValue 的invokeMethod(_:withArguments:)或者call(withArguments:)也可以工作。

NSHipster 有一篇关于该框架的精彩文章。你可以参考一下。


在我检查了文档和 iOS 13+ 新 API 之后。有什么可以确认的。那就是您可以使用JSObjectMakeDeferredPromisecreate Promise,这是 C 方式。为方便起见,我通过 Swift 方式创建所需的函数并导出到 C。所以答案是:

let context = JSContext()
var ref = context.jsGlobalContextRef
context.evaluateScript("function resolve(value) { console.log(value); };")
var resolve = context.objectForKeyedSubscript("resolve")?.jsValueRef
context.evaluateScript("function reject(reason) { console.log(reason); };")
var reject = context.objectForKeyedSubscript("reject")?.jsValueRef

var exception: JSValueRef?
var promise = JSObjectMakeDeferredPromise(ref, &resolve, &reject, &exception)

(lldb) po promise
▿ Optional<OpaquePointer>
  ▿ some : 0x000000010c74ae78
    - _rawValue : (Opaque Value)

(lldb) p promise
(JSObjectRef?) $R4 = 0x000000010c74ae78

我不想深入研究。这只是意味着您可以将 JavaScriptCore 与 C 接口一起使用。并且JSValue拥有 iOS 13+ 新的 C 风格的 init 方法.init(newPromiseIn:fromExecutor:)。看起来一样,但我不太确定。

如果你坚持使用 C API。这篇文章可能会有所帮助。

PS该console.log方法是手工原生导出日志的方法。


推荐阅读