概述:当iOS客户端中webView 与js交互,在主线程执行js脚本时,而js脚本存在alert()、confirm()、prompt()这三种弹窗时会造成iOS界面卡死。

1、造成卡死时的代码如下:

NSString *script = "doSubmit()";

(1)

iOS端实现:

[self.webView stringByEvaluatingJavaScriptFromString:script];

js实现:

doSubmit = function (obj) {

if (confirm("已存在故障反馈,确认继续提交?")){

flag = true;

} else {

flag = false;

}

return flag;

}

(2)

iOS端实现:

dispatch_async(dispatch_get_main_queue(), ^{

[self.webView stringByEvaluatingJavaScriptFromString:script];

});

js实现:

doSubmit = function (obj) {

if (confirm("已存在故障反馈,确认继续提交?")){

flag = true;

} else {

flag = false;

}

return flag;

}

(3)

iOS端实现:

[self.webView performSelectorOnMainThread:@selector(stringByEvaluatingJavaScriptFromString:) withObject:script waitUntilDone:YES];

js实现:

doSubmit = function (obj) {

if (confirm("已存在故障反馈,确认继续提交?")){

flag = true;

} else {

flag = false;

}

return flag;

}

2、卡死原因分析:

(1)第一种情况由于iOS端的js脚本是在主线程执行,而js的三种弹窗上的按钮操作也必须在主线程完成,alert()、confirm()、prompt()这三种弹窗会中断js脚本的执行,也就是弹窗上面的按钮没有点击响应结果前,if (confirm("已存在故障反馈,确认继续提交?"))后面的代码将不会执行,此时 [self.webView stringByEvaluatingJavaScriptFromString:script];占用着主线程一直在等待return flag;语句执行完成。这将造成:点击弹窗的按钮在等js执行return 语句拿到主线程刷新UI,而占用主线程的一直在等js脚本执行完成的互相等待。

(2)第二种dispatch_async()造成卡死的原因如下:

js axios阻塞响应 js实现confirm阻塞_js axios阻塞响应

dispatch_async的实现代码.jpg

上图是dispatch_async()的实现代码

dispatch_atomic_inc和dispatch_atomic_dec是原子操作函数;

由上图可知dispatch_async执行任务的时候会对当前工作的线程加原子操作,而原子操作是不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 的线程切换, 任务不执行完, 工作线程是不会被中断的。所以主线程执行的js脚本不会被中断。

js中弹窗中断代码的作用, 弹窗下面的代码在弹窗上面的按钮点击之前是不会被执行到的。所以它们进入了相互等待资源中。

(3)

performSelectorOnMainThread: withObject: waitUntilDone:

这个方法中waitUntilDone:的参数官方注释wait的意思:

A Boolean that specifies whether the current thread blocks until after the specified selector is performed on the receiver on the main thread.

Specify YES to block this thread;

otherwise, specify NO to have this method return immediately.

如果wait是YES,会锁死主线程,直到selector方法执行完. 如果为NO,selector方法会立即返回。

而iOS 客户端实现的代码是YES,所以存在竞争主线程,造成界面卡死的问题。

3、卡死问题处理:

或:iOS端代码实现修改:

[self.webView performSelectorOnMainThread:@selector(stringByEvaluatingJavaScriptFromString:) withObject:script waitUntilDone:NO];

或:js代码实现修改:

doSubmit = function (obj) {

setTimeout(function() {

if (confirm("已存在故障反馈,确认继续提交?")){

flag = true;

} else {

flag = false;

}

return flag;

},-1);

}

以上只要有一端做修改即可。