How to pause an asynchronous Swift function until a callback is called when a result is available from another library(JavaScriptCore)

mrtksn

I want to use Vapor(a Web framework made with Swift) and JavaScriptCore(Apple's JS engine) to process some requests. Vapor supports async/await, which makes handling HTTP requests very easy, like this:

func routes(_ app: Application) throws {
    
    app.get("myrequestpath") { req async throws -> String in

        let result = await myAsyncFunction()        

        return result
    }

}

myAsyncFunction() handles the processing to return the desired result and some of the processing I do is in JavaScript, executed in JavaScriptCore. I have no problem with interacting with JavaScriptCore, I can call the JS functions and JS can call the Swift functions I give access to.

I want to be able to initiate a JS function from Swift, wait for the async code in JS to finish its job and then pass the result to Swift async code for further processing.

The problem is, JavaScript itself has async/await that works slightly differently than the one in Swift.

In JavaScript, async functions return a Promise object that later resolves once the code block inside it calls the resolve or reject functions.

As a result, I can't simply await a JS function because it will return a Promise object right away and Swift will continue execution:

func myAsyncFunction() async -> Bool {
    
    let jsContext = JSEngine().evaluateScript(myJSCode)
    let result = await jsContext.objectForKeyedSubscript("myAsyncJSFunction").call(withArguments: [data])
    return result
}

I would love to do that but I can't, the result will be a Promise object that doesn't mean anything in Swift(right).

As a result, what I need is, to write an async Swift function that pauses until the JS function calls back the resolve or reject function.

Something like this maybe?:

func myAsyncFunction() async -> Bool {
    
    let jsContext = JSEngine().evaluateScript(myJSCode)
    let result = await {
        /*
         A function that I can pass to JS and the JS can call it when it's ready, return the result to Swift and continue execution
         */
        
    }
    return result
}

Any ideas how to achieve that?

mrtksn

Okay, as it turns out, Swift async/await concurrency does have a support continuation. It's not mentioned on the main article on Swift documentation and most 3rd party articles bill this feature as a way to integrate the old callback centric concurrency with the new async/await API, however this is much more useful than simply integrating the old code with the new one.

Continuation provides you with an asynchronous function that can be called from within your async function to pause execution with await until you explicitly resume it. That means, you can await input from the user or another library that uses callback to deliver the results. That also means, you are entirely responsible to correctly resume the function.

In my case, I'm providing the JavaScriptCore library with a callback that resumes execution from Swift, which is called by an async JavaScript code upon completion.

Let me show you. This is a JS code that asynchronously processes a request:

async function processRequest(data, callback){
  try {
      let result = await myAsyncJSProcessing(data)
      callback(result, null)
    } catch(err) {
      callback(null, err)
    }
}

To be able to pause the Swift execution until the JS code finishes processing and continue once the JS calls the callback with the data, I can use continuation like this:

//proccessRequestSync is a synchronous Swift function that provides the callback that the JS will call.
// This is also the function with a callback that Swift will wait until the callback is called
// self.engine is the initiated JavaScriptCore context where the JS runs.
// We create the callback that we will pass to the JS engine as JSValue, then pass it to the JS function as a parameter. Once the JS is done, calls this function.
func proccessRequestSync(_ completion : @escaping (Result<JSValue, Error>) -> Void){
            let callback : @convention(block) (JSValue, JSValue) -> Void = { success, failure in
                completion(.success(success))
            }
            let jsCallback = JSValue(object: callback, in: self.engine)
            self.engine?.objectForKeyedSubscript("processRequest").call(withArguments: ["some_data", jsCallback])
        }
// Then we create the async function that will be paused using continuation.
// We can integrate the function into the rest of our async Swift.
func proccessRequestAsync() async throws -> JSValue {
            //Continuation happens right here. 
            return try await withCheckedThrowingContinuation({ continuation in
                proccessRequestSync { result in
                    // This is the closure that will be called by the JS once processing is finished
                    // We will explicitly resume the execution of the Swift code by calling continuation.resume()
                    switch result {
                                case .success(let val):
                                    continuation.resume(returning: val)
                                case .failure(let error):
                                    continuation.resume(throwing: error)
                                }
                }
            })
        }
        
if let result = try? await proccessRequestAsync()

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

How to wait until a asynchronous function finish running then only run continue another function in swift?

From Dev

Swift Function called from inside Asynchronous closure also Asynchronous?

From Javascript

How to return value from an asynchronous callback function?

From Dev

How to prop check when a function is called from another function?

From Dev

Jest, expect another asynchronous callback not to be called

From Dev

asyncio how to pause coroutine until send is called

From Dev

How to stop function from returning new result until x seconds is over, and return previous result if called too soon

From Dev

How to pass a parameter into an https function when it's called from swift?

From Dev

How to invoke AWS lambda function from another lambda function and return without waitng for the called lambda result

From Dev

How to pass data from one asynchronous to another asynchronous function in AngularJS

From Dev

How to assign the value returned by a callback function to a variable? Couldn't understand from another ques. about asynchronous call

From Dev

Dart Component: How to return result of asynchronous callback?

From Dev

Add A Callback To A Prebuilt Asynchronous Function Swift iOS

From Dev

How to yield from an async callback called from a generator function?

From Javascript

How to make a function wait until a callback has been called using node.js

From Dev

How to return the result of serde_json::from_str when called with a String that will be dropped at the end of the function?

From Dev

how to return multiple values from an asynchronous function to use in another function

From Dev

How to wait for the iteration to finish when pushing result of a callback function into an array

From Dev

Javascript pause - disable a function until another one finishes

From Java

How to hold the thread execution until asynchronous thread return callback

From Dev

How to pause the program until we get the result of asynchoronous method?

From Dev

How to avoid the error "staticCFunction must take an unbound..." in Kotlin Native when a Kotlin callback function is called from C?

From Dev

Tkinter passing callback function result to another class

From Dev

How to store the variable of an asynchronous callback function

From Dev

Meteor: How to catch asynchronous callback function errors

From Dev

How do I suppress lines of code inside one function when that function is being called from within another function?

From Dev

How to write an asynchronous recursive walkdir function with an asynchronous callback

From Java

Function crashing app when called from another class but not when called from MainActivity.java

From Dev

Node.js how to return an array from within a asynchronous callback for use in another file

Related Related

  1. 1

    How to wait until a asynchronous function finish running then only run continue another function in swift?

  2. 2

    Swift Function called from inside Asynchronous closure also Asynchronous?

  3. 3

    How to return value from an asynchronous callback function?

  4. 4

    How to prop check when a function is called from another function?

  5. 5

    Jest, expect another asynchronous callback not to be called

  6. 6

    asyncio how to pause coroutine until send is called

  7. 7

    How to stop function from returning new result until x seconds is over, and return previous result if called too soon

  8. 8

    How to pass a parameter into an https function when it's called from swift?

  9. 9

    How to invoke AWS lambda function from another lambda function and return without waitng for the called lambda result

  10. 10

    How to pass data from one asynchronous to another asynchronous function in AngularJS

  11. 11

    How to assign the value returned by a callback function to a variable? Couldn't understand from another ques. about asynchronous call

  12. 12

    Dart Component: How to return result of asynchronous callback?

  13. 13

    Add A Callback To A Prebuilt Asynchronous Function Swift iOS

  14. 14

    How to yield from an async callback called from a generator function?

  15. 15

    How to make a function wait until a callback has been called using node.js

  16. 16

    How to return the result of serde_json::from_str when called with a String that will be dropped at the end of the function?

  17. 17

    how to return multiple values from an asynchronous function to use in another function

  18. 18

    How to wait for the iteration to finish when pushing result of a callback function into an array

  19. 19

    Javascript pause - disable a function until another one finishes

  20. 20

    How to hold the thread execution until asynchronous thread return callback

  21. 21

    How to pause the program until we get the result of asynchoronous method?

  22. 22

    How to avoid the error "staticCFunction must take an unbound..." in Kotlin Native when a Kotlin callback function is called from C?

  23. 23

    Tkinter passing callback function result to another class

  24. 24

    How to store the variable of an asynchronous callback function

  25. 25

    Meteor: How to catch asynchronous callback function errors

  26. 26

    How do I suppress lines of code inside one function when that function is being called from within another function?

  27. 27

    How to write an asynchronous recursive walkdir function with an asynchronous callback

  28. 28

    Function crashing app when called from another class but not when called from MainActivity.java

  29. 29

    Node.js how to return an array from within a asynchronous callback for use in another file

HotTag

Archive