setTimeout阻止eventloop

时间:2016-07-04 02:13:11

标签: javascript node.js settimeout

我正在阅读一篇文章,声明要创建非阻塞/异步功能,setTimeout的使用至关重要。我认为传递给setTimeout的函数在后台运行。然后我在另一篇文章中读到setTimeout在触发函数时阻止了事件循环。所以,我尝试了以下功能进行测试。

function getit(cb) {
var i = 0;
 setTimeout(function() {
  while (i < 200000) { i++; console.log(i); }
  cb(i); 
 } , 1000); 
 console.log(i); 
}

getit(function(message) { console.log(message); });

显然,当1000ms通过并且是时候执行该功能时,线程被阻塞并且我的浏览器冻结。我的问题是,如果异步代码不应该阻塞线程,那么当时间过去而不是在后台执行函数时,这怎么可能呢?看起来这只是一个延迟,但最终,该功能将逐行执行并阻止循环。有什么我想念或混淆的东西吗?

2 个答案:

答案 0 :(得分:2)

首先必须了解JavaScript,在大多数情况下,单线程。无论是否异步,一旦您的脚本开始执行,它就会阻止所有内容。

上面的示例因此在1000ms后进入忙碌的循环并开始阻止其他执行。

为了解决这个问题,繁忙的街区必须分解为&#34; yield&#34; (Java术语)执行其他任务的周期。

E.g:

&#13;
&#13;
    function getit(cb) {
        setTimeout(function() {
            lessBlockingGetIt(cb);
        } , 1000);
    }
    
    // Note that this is only less blocking, and much slower
    function lessBlockingGetIt(cb) {
    
        // Instead of directly looping, we batch-loop with a setTimeout
        var numberOfTimes = 1000; // Reduced the size so it doesn't block like forever
        var batchSize = 10;
    
        function executeBatch(start) {
    
            // We use a zero-timeout function is achieve a "yield" behavior so other tasks in the event loop can execute
            setTimeout(function(){
                var i = start, j;
                for(j = 0; i < numberOfTimes && j < batchSize; i++, j++){
                    console.log(i);
                }
                if(i < numberOfTimes){
                    executeBatch(i);
                } else {
                    cb(i);
                    console.log(i);
                }
            }, 0);
    
        }
      
        // Start the recursion loop
        executeBatch(0);
    
    }
    
    getit(function(message) { console.log(message); });
&#13;
&#13;
&#13;

但是,您会注意到执行速度要慢得多,并且浏览器的响应速度明显低于真正的多线程。

要实现真正的多线程,您需要使用网络工作者http://www.w3schools.com/html/html5_webworkers.asp

答案 1 :(得分:1)

Node.js执行模型可以用以下代码表示:

function main() {
   while(running) {
     var timerCb = getNextTimedoutCallback();
     if( timerCb ) timerCb(); // execute it

     var ioCb = getNextCompleteIOOperationCallback();
     if( ioCb ) ioCb(); // execute it
   }
}

一切都在单线程中运行。 IO操作在工作线程中运行,并填充已完成IO操作的内部队列。 getNextCompleteIOOperationCallback();只是从队列中拉出完成的操作。

同样地,setTimeout()只是将函数与其结束时间一起推送到定时器队列(按结束时间排序)。并getNextTimedoutCallback()拉下一个过期的超时(如果有的话)。

如您所见,定时器功能或IO回调可以阻止整个事情。 这被称为cooperative multitasking