Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setTimeout 和 Promise 两个异步的区别 #11

Open
lixingdecai opened this issue Dec 7, 2017 · 3 comments
Open

setTimeout 和 Promise 两个异步的区别 #11

lixingdecai opened this issue Dec 7, 2017 · 3 comments
Labels

Comments

@lixingdecai
Copy link
Owner

lixingdecai commented Dec 7, 2017

看看下面JS,执行后的结果是什么

setTimeout(function(){console.log(4)},0); 
new Promise(function(resolve){ 
    console.log(1) 
    for( var i=0 ; i<10000 ; i++ ){
            i==9999 && resolve() 
        }
    console.log(2) 
}).then(function(){
    console.log(5) 
}); 
console.log(3);

答案是 1 2 3 5 4;
而不是 1 2 3 4 5;

async function async1() {
 console.log('async1 start');
 await async2();
 console.log('async1 end');
}
async function async2() {
 console.log('async2');
}
console.log('script start');
setTimeout(function() {
   console.log('setTimeout');
}, 0);  

async1();
new Promise(function(resolve) {
   console.log('promise1');
   resolve();
 }).then(function() {
   console.log('promise2');
});
console.log('script end');

// =============
答案
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

原因:
有一个事件循环,但是任务队列可以有多个。

一个浏览器环境(unit of related similar-origin browsing contexts.)只能有一个事件循环(Event loop),而一个事件循环可以多个任务队列(Task queue),每个任务都有一个任务源(Task source)。
相同任务源的任务,只能放到一个任务队列中。不同任务源的任务,可以放到不同任务队列中。

整个script代码,放在了macrotask queue中,setTimeout也放入macrotask queue。但是,promise.then放到了另一个任务队列microtask queue中。

这两个任务队列执行顺序如下,取1个macrotask queue中的task,执行之。然后把所有microtask queue顺序执行完,再取macrotask queue中的下一个任务。

代码开始执行时,所有这些代码在macrotask queue中,取出来执行之。后面遇到了setTimeout,又加入到macrotask queue中,然后,遇到了promise.then,放入到了另一个队列microtask queue。

等整个execution context stack执行完后,下一步该取的是microtask queue中的任务了。因此promise.then的回调比setTimeout先执行。

image

microtask(Job) > macrotask(Task);
microtask: process.nextTick > Promise > Object.observe > MutationObserver
macrotask: setTimeout > setInterval > setImmediate

Event Loop 是一种程序结构,是实现异步的一种机制,包含:执行队列 和 任务队列。

参考资料
Node.js的event loop及timer/setImmediate/nextTick
Process.nextTick 和 setImmediate 的区别
你不知道的setTimeout

@Griffin23
Copy link

async function async1() {
 console.log('async1 start');
 await async2();
 console.log('async1 end');
}
async function async2() {
 console.log('async2');
}
console.log('script start');
setTimeout(function() {
   console.log('setTimeout');
}, 0);  

async1();
new Promise(function(resolve) {
   console.log('promise1');
   resolve();
 }).then(function() {
   console.log('promise2');
});
console.log('script end');

答案应该是

script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

因为挂起的async1()会先进入队列。
async1()执行到await async2()时挂起,入队。
然后执行new Promise(),遇到resolve时入队。

@lixingdecai
Copy link
Owner Author

@Griffin23 谢谢指正

@lixingdecai
Copy link
Owner Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants