簡體   English   中英

當另一個庫(JavaScriptCore)的結果可用時,如何暫停異步 Swift 函數直到調用回調

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

我想使用 Vapor(一個用 Swift 制作的 Web 框架)和 JavaScriptCore(Apple 的 JS 引擎)來處理一些請求。 Vapor 支持 async/await,這使得處理 HTTP 請求變得非常容易,如下所示:

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

        let result = await myAsyncFunction()        

        return result
    }

}

myAsyncFunction() 處理返回所需結果的處理,我做的一些處理是在 JavaScript 中,在 JavaScriptCore 中執行。 我與 JavaScriptCore 交互沒有問題,我可以調用 JS 函數,而 JS 可以調用我授予訪問權限的 Swift 函數。

我希望能夠從 Swift 啟動一個 JS 函數,等待 JS 中的異步代碼完成其工作,然后將結果傳遞給 Swift 異步代碼進行進一步處理。

問題是,JavaScript 本身具有 async/await,其工作方式與 Swift 中的稍有不同。

在 JavaScript 中,異步函數返回一個 Promise 對象,該對象稍后會在其中的代碼塊調用解析或拒絕函數時解析。

結果,我不能簡單地等待一個 JS 函數,因為它會立即返回一個 Promise 對象,而 Swift 將繼續執行:

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

我很想這樣做,但我做不到,結果將是一個 Promise 對象,它在 Swift 中沒有任何意義(對)。

因此,我需要編寫一個異步 Swift 函數,該函數會暫停,直到 JS 函數回調解析或拒絕函數。

可能是這樣的?:

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
}

任何想法如何實現這一目標?

好吧,事實證明,Swift async/await 並發確實支持繼續。 Swift 文檔的主要文章中沒有提到它,並且大多數 3rd 方文章都將此功能稱為將舊的以回調為中心的並發與新的 async/await API 集成的方式,但是這比簡單地將舊代碼與新的一個。

Continuation 為您提供了一個異步函數,可以在您的 async 函數中調用該函數以使用 await 暫停執行,直到您顯式恢復它為止。 這意味着,您可以等待來自用戶或另一個使用回調來傳遞結果的庫的輸入。 這也意味着,您完全有責任正確恢復該功能。

就我而言,我為 JavaScriptCore 庫提供了一個回調,該回調從 Swift 恢復執行,完成后由異步 JavaScript 代碼調用。

我來給你展示。 這是一個異步處理請求的 JS 代碼:

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

為了能夠暫停 Swift 執行,直到 JS 代碼完成處理並在 JS 使用數據調用回調后繼續,我可以像這樣使用延續:

//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()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM