单线程异步
js是单线程异步的,单线程就是同一时间只做一件事,原因是避免dom操作的冲突,设想一下js是多线程的,然后多线程上的js都要操作一个dom,就会产生冲突。而仅仅是单线程的话,如果有一个请求要耗费很长时间时,浏览器就会卡在那里等待,这样当然也不行,所以有了异步。
event-loop
那么异步要怎么运行?这就是接下来要说的event-loop(事件轮询)
event-loop是js的运行机制,其执行的方式如下:

1.同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
2.当指定的事情完成时,Event Table会将这个函数移入Event Queue。
3.主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
4.上述过程会不断重复,也就是常说的Event Loop(事件循环)。
除了广义的同步任务和异步任务,我们对任务有更精细的定义:
macro-task(宏任务):包括整体代码script,setTimeout,setInterval
micro-task(微任务):Promise,process.nextTick
注意:任务队列分微任务和宏任务,先执行全部的微任务,再执行宏任务。
接下来看一下下面的案例:
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');
分析:
1、整个代码块作为一个宏任务,进入主线程,async1和async2函数定义,遇到console.log输出script start;
2、遇到setTimeout() ,把它的回调函数放入宏任务(setTimeout1)。
微任务 | 宏任务 |
---|---|
setTimeout1 |
3、遇到执行async1(), 执行async1,遇到console.log输出async1 start;
4、然后遇到await async2(),执行async2();
5、看到console.log输出async2,之后没有返回值,结束函数,返回undefined,返回到async1的执行上下文的await undefined,由于async函数使用await后得语句会被放入一个回调函数中,所以把下面的放入微任务中。
微任务 | 宏任务 |
---|---|
async1=> await后面的语句 | setTimeout1 |
6、结束async1,返回全局上下文,遇到Promise构造函数,里面的执行函数立马执行, 输出promise1, 之后的回调函数进入微任务;
微任务 | 宏任务 |
---|---|
async1=> await后面的语句 | setTimeout1 |
new Promise() => 后的then |
执行完Promise(),遇到console.log,输出script end,这里一个宏任务代码块执行完毕。
7、主线程现在空闲下来后,执行事件队列中的微任务,遇到new Promise()后面的回调函数,执行代码,输出promise2(这里2个微任务的优先级,promise高于async)。
8、看到async1中await后面的回调函数,执行代码,输出async1 end
9、此时微任务中的队列为空,开始执行队列中的宏任务,遇到console.log,输出setTimeout。
最后,执行完成,最后结果为
script start
async1 start
async2
promise1
script end
promise2
async1 end
setTimeout
网友评论