我正在阅读一篇文章,声明要创建非阻塞/异步功能,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通过并且是时候执行该功能时,线程被阻塞并且我的浏览器冻结。我的问题是,如果异步代码不应该阻塞线程,那么当时间过去而不是在后台执行函数时,这怎么可能呢?看起来这只是一个延迟,但最终,该功能将逐行执行并阻止循环。有什么我想念或混淆的东西吗?
答案 0 :(得分:2)
首先必须了解JavaScript,在大多数情况下,单线程。无论是否异步,一旦您的脚本开始执行,它就会阻止所有内容。
上面的示例因此在1000ms后进入忙碌的循环并开始阻止其他执行。
为了解决这个问题,繁忙的街区必须分解为&#34; yield&#34; (Java术语)执行其他任务的周期。
E.g:
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;
但是,您会注意到执行速度要慢得多,并且浏览器的响应速度明显低于真正的多线程。
要实现真正的多线程,您需要使用网络工作者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。