【问题标题】:WKWebView: trying to query javascript synchronously from the main threadWKWebView:尝试从主线程同步查询 javascript
【发布时间】:2015-04-07 22:03:33
【问题描述】:

有没有办法从主线程同步查询javascript?

使用带有回调参数的异步函数从本机代码中查询Javascript来处理响应:

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

通常可以通过暂停线程并使用信号量控制执行来将异步行为变为同步:

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

...但是在这种情况下,因为completionHandler 在主线程上总是调用,所以代码死锁因为completionHandler 块永远不会执行(主线程被dispatch_semaphore_wait 在最后一行)

有什么建议吗?

编辑

我宁愿不阻塞主线程来执行该代码。但是如果不将我的 API 从同步更改为异步,我就无法从主线程中解耦,并在堆栈中一直产生多米诺骨牌效应(例如,从 let ua = computeUserAgent()computeUserAgent() {(ua: String)->Void in /*Use ua value here */})。所以我需要在两种都有缺点的方法之间进行选择,我宁愿选择不会弄乱我的内部 API 的方法,尤其是对于像查找用户代理这样微不足道的任务。

【问题讨论】:

  • 当然,这里有个建议:不要那样做。使用提供的功能并继续前进。不要试图破坏异步功能。如果您阻止主线程等待,用户将无法与您的应用程序交互,并且看门狗进程可能会杀死您的应用程序。有一些合法的方法可以让用户暂时无法做任何事情,但这不是其中之一。
  • @matt,我认为您在这里忽略了要点:据我了解,completionHandler 似乎总是在主线程上运行的事实使得难以实现 evaluateJavascript 的同步抽象调用...同步并不意味着“在主线程上”
  • @StephanePhilipakis 这不是他要求的。他希望这个主线程(见问题的标题)并且是同步的。他不应该这样。我的评论说“不想那样”。
  • @matt 你是对的,我确实错过了重点:)
  • 所以我用本机代码包装了一个 Web 应用程序,以提供持久性和对系统剪贴板的访问。因此,我的数据模型存在于 JavaScript 中。当需要保存、导出、复制、剪切或打印时,我需要能够调用 JavaScript 来响应该操作并返回结果。具体来说,对于导出和打印,我必须在“UI”操作的中间得到结果。如果有办法 a) 异步执行此操作或 b) 在主线程之外执行此操作,我看不到它。有什么建议吗?

标签: ios ios8 grand-central-dispatch semaphore wkwebview


【解决方案1】:

如果您必须这样做...

正如对this answer 的评论中所建议的,您可以像这样围绕信号量等待运行一个紧密的循环。

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

【讨论】:

  • Kludgy 但这是对问题的有效答案,谢谢!还不太确定我将在我的项目中使用什么(特别是,@matt 在问题的评论部分提出了一些很好的观点)但我明天会接受这个答案,除非有更好的答案出现
  • 你好。我测试了您的解决方案,但它不起作用。 completionHandler 仍然被阻塞,仍然没有执行:/
  • 会导致崩溃!
猜你喜欢
  • 1970-01-01
  • 2017-09-20
  • 1970-01-01
  • 2021-01-31
  • 1970-01-01
  • 2013-10-14
  • 1970-01-01
  • 2014-11-07
  • 1970-01-01
相关资源
最近更新 更多