JSFiddle - React, Tailwind, and code Playground

by camnpr

HTML

由于运算时间长,触发了浏览器长时间运行脚本限制,UI线程被阻塞了。所以,迭代中有大规模运算,100ms内完成不了的任务可以进行拆分,让javascript短暂让出UI线程控制权,以执行其他任务。

要短暂让出UI线程控制权,可以使用setTimeout。

setTimeout的时间精度:
javascript中定时器是有时间精度的, IE9(非充电模式)、IE8及其以下版本的时间精度是15.6ms;IE9(充电模式)下是4ms;其他浏览器一般也是4ms,低于4ms会降低电池使用寿命。所以,setTimeout(fn, 0)并非马上执行的,其执行时机取决于时间精度。

为了解决setTimeout在不同浏览器的时间精度问题,W3C因此引入了新的setImmediate()函数。setImmediate与setTimeout类似,setImmediate会在UI线程空闲时将任务插入到队列并执行,我们不再需要关心时间精度的影响。并且,setImmediate执行起来比setTimeout(fn, 0)要快。

由于任务运行时间的不确定性,在迭代运算中,可以加上运算时间监控,决定此次迭代是否需要拆分任务。

我们来做一个简单的demo,0~n的累加运算:

JavaScript

// o~n的累加运算
var test = function(n, callback) {
    var result = 0;
    var i = 0;
    (function() {
        var st = +new Date();
        for (; i < n; i++) {
            if ((+new Date()) - st < 100) {
                result++;
            } else {
                setTimeout(arguments.callee, 0); // 运算时间差大于100ms时,中断运算,让出UI线程控制权
                return;
            }
        }
        callback && callback();
    })();
};
alert(test(10000000, showResult)); // 1千万次累加


//由于额外的流程控制开销,setTimeout方式相对直接运算会消耗更多的时间,好处是UI线程不再阻塞,可以处理更多的任务。

//除了setTimeout方式,将需要长时间运算的操作放到flash里面进行也是一种解决方案,避开javascript单一线程的影响。