javascript - Get value from JS Promiseasync function from within a JSContext - Stack Overflow

I'm executing a JavaScript SDK from within a JSContext, I can't get values out of any of the

I'm executing a JavaScript SDK from within a JSContext, I can't get values out of any of the SDK's asynchronous functions however. I can get a JavaScript promise out of the JSContext, but I can't figure out how to resolve it. I have tried many ways of getting the value from the Promise, but every one has failed.

If I try something like the following I get [object Promise] back:

return self.jsContext.evaluateScript("new Promise(resolve => { setTimeout(300, () => resolve([1, 2, 3])) })")!

If I chain then directly onto the JS I get [object Promise] still:

return self.jsContext.evaluateScript("new Promise(resolve => { setTimeout(300, () => resolve([1, 2, 3])) }).then(val => val.json())")

If I try to invoke the method from Swift, I get still get [object Promise]:

let jsPromise = self.jsContext.evaluateScript("new Promise(resolve => { setTimeout(300, () => resolve([1, 2, 3])) })")
let promiseResult = jsPromise?.invokeMethod("then", withArguments: ["val => { return val.json() }"])
return promiseResult!

If I declare a JS variable outside of the Promise, then pass the value to it from a Swift-invoked then call, I get the original value set to it (as expected but worth a try):

self.jsContext.evaluateScript("let tempVar = 'Nothing has happened yet!'")
let jsPromise = self.jsContext.evaluateScript("new Promise(resolve => { setTimeout(300, () => resolve([1, 2, 3])) })")
let promiseResult = jsPromise?.invokeMethod("then", withArguments: ["val => { tempVar = val }"])
let tempVar = self.jsContext.evaluateScript("tempVar")
return tempVar!

If I try and use top-level await and resolve the Promise to a variable, then pull that variable out of the JSContext, IU get a EXC_BAD_INSTRUCTION error:

let jsPromise = self.jsContext.evaluateScript("let someVar = await new Promise(resolve => { setTimeout(300, () => resolve([1, 2, 3])) })")
return self.jsContext.evaluateScript("someVar")!

Thanks in advance, and sorry if I'm missing something, still very new to Swift.

I'm executing a JavaScript SDK from within a JSContext, I can't get values out of any of the SDK's asynchronous functions however. I can get a JavaScript promise out of the JSContext, but I can't figure out how to resolve it. I have tried many ways of getting the value from the Promise, but every one has failed.

If I try something like the following I get [object Promise] back:

return self.jsContext.evaluateScript("new Promise(resolve => { setTimeout(300, () => resolve([1, 2, 3])) })")!

If I chain then directly onto the JS I get [object Promise] still:

return self.jsContext.evaluateScript("new Promise(resolve => { setTimeout(300, () => resolve([1, 2, 3])) }).then(val => val.json())")

If I try to invoke the method from Swift, I get still get [object Promise]:

let jsPromise = self.jsContext.evaluateScript("new Promise(resolve => { setTimeout(300, () => resolve([1, 2, 3])) })")
let promiseResult = jsPromise?.invokeMethod("then", withArguments: ["val => { return val.json() }"])
return promiseResult!

If I declare a JS variable outside of the Promise, then pass the value to it from a Swift-invoked then call, I get the original value set to it (as expected but worth a try):

self.jsContext.evaluateScript("let tempVar = 'Nothing has happened yet!'")
let jsPromise = self.jsContext.evaluateScript("new Promise(resolve => { setTimeout(300, () => resolve([1, 2, 3])) })")
let promiseResult = jsPromise?.invokeMethod("then", withArguments: ["val => { tempVar = val }"])
let tempVar = self.jsContext.evaluateScript("tempVar")
return tempVar!

If I try and use top-level await and resolve the Promise to a variable, then pull that variable out of the JSContext, IU get a EXC_BAD_INSTRUCTION error:

let jsPromise = self.jsContext.evaluateScript("let someVar = await new Promise(resolve => { setTimeout(300, () => resolve([1, 2, 3])) })")
return self.jsContext.evaluateScript("someVar")!

Thanks in advance, and sorry if I'm missing something, still very new to Swift.

Share Improve this question edited Mar 9, 2023 at 22:09 iUrii 13.9k2 gold badges39 silver badges62 bronze badges asked Sep 12, 2017 at 16:53 JmJJmJ 2,1283 gold badges31 silver badges53 bronze badges 1
  • What do you see in the object if you debug it with a breakpoint? – Jonas Commented Sep 16, 2017 at 18:06
Add a ment  | 

2 Answers 2

Reset to default 4 +50

There are issues while mocking the Promise work flow inside JSContext. The functions like setTimout,setInterval etc. are not available in JSContext.

However, you can call Swift code from Javascript by passing block into the JSContext. Here's a code snippet, that shows how you can find out errors in JSContext.

var logValue = "" {
    didSet {
        print(logValue)
    }
}
//block we can pass to JSContext as JS function
let showLogScript: @convention(block) (String) -> Void = { value in
    logValue = value
}
let jsContext = JSContext()

//set exceptionHandler block
jsContext?.exceptionHandler = {
    (ctx: JSContext!, value: JSValue!) in
    print(value)
}
//make showLog function available to JSContext
jsContext?.setObject(unsafeBitCast(showLogScript, to: AnyObject.self), forKeyedSubscript: "showLog" as (NSCopying & NSObjectProtocol))

jsContext!.evaluateScript("showLog('this is my first name')") //this works
jsContext!.evaluateScript("showLog(setTimeout.name)") //it has issue

To get resolved values (and rejected errors) from asynchronous javascript code you should operate with javascript's Promise and its then method:

Promise.prototype.then()

then(onFulfilled)
then(onFulfilled, onRejected)

then(
  (value) => { /* fulfilment handler */ },
  (reason) => { /* rejection handler */ },
)

We can call then with the fulfilment handler which provides a resolved value from above, so let's implement that:

let script = """
new Promise(resolve => {
    setTimeout(() => resolve([1, 2, 3]), 300)
})
"""
let promise = context.evaluateScript(script)

let onFulfilled: @convention(block) (JSValue) -> Void = {
    print($0) // Prints: 1,2,3
}
promise?.invokeMethod("then", withArguments: [unsafeBitCast(onFulfilled, to: JSValue.self)])

This approach works for async functions as well because they operate with Promise under the hood, for instance:

let script = """
async function load() {
    return [1, 2, 3];
}
"""
context.evaluateScript(script)

let promise = context.evaluateScript("load()")

let onFulfilled: @convention(block) (JSValue) -> Void = {
    print($0) // Prints: 1,2,3
}
promise?.invokeMethod("then", withArguments: [unsafeBitCast(onFulfilled, to: JSValue.self)])

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745638045a4637516.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信