简体   繁体   中英

WKWebView: trying to query javascript synchronously from the main thread

Is there any way to query the javascript synchronously from the main thread?

Javascript is queried from the native code using an asynchronous function with a callback parameter to handle the response:

func evaluateJavaScript(_ javaScriptString: String, completionHandler completionHandler: ((AnyObject!, NSError!) -> Void)?)

Asynchronous behavior can usually be turned synchronous by pausing the thread & controlling execution with a semaphore:

// Executing in the main thread
let sema = dispatch_semaphore_create(0)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
// Background thread
  self.evaluateJavaScript("navigator.userAgent", completionHandler: { (value:AnyObject!, error: NSError!) -> Void in
    if let ua = value as? String {
      userAgent = ua
    } else {
      ERROR("ERROR There was an error retrieving the default user agent, using hardcoded value \(error)")
    }
    dispatch_semaphore_signal(sema)
  })
}
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)

...however in this case, because the completionHandler is always called on the main thread, the code deadlocks because the completionHandler block will never execute (main thread was paused by the dispatch_semaphore_wait on the last line)

Any suggestions?

EDIT

I would rather not be blocking the main thread to execute that code. But I can't decouple from the main thread without changing my APIs from synchronous to asynchronous, with a domino effect all the way up the stack (eg from let ua = computeUserAgent() to computeUserAgent() {(ua: String)->Void in /*Use ua value here */} ). So I need to choose between 2 approaches that both have downsides, and I would rather pick the approach that doesn't mess up my internal APIs, especially for a task as trivial as looking up the user agent.

If you must do this...

As suggested in a comment to this answer you could run a tight loop around your semaphore wait like this.

while (dispatch_semaphore_wait(sema, DISPATCH_TIME_NOW)) { 
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 
                             beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM