javascript事件循环
Javascript是一门单线程语言,也就是说js在处理任务的时候,所有的任务只能在一个线程上排队等待被执行,如果其中有一个任务耗时较长的时候,其他的的任务就只能等,就如同下图,你懂得!
所以,这个时候就用到了异步任务,异步任务是包含了独立于主线程之外的 宏任务 和 微任务 。
宏任务 和 微任务
宏任务和微任务都是独立于主执行栈之外的两个队列,在概念上可以划分在异步队列里,而这些队列的执行有事件循环(EventLoop)来处理。
- 宏任务包括:包括整体代码 script、setTimeout、setInterval、I/O、UI render(ajax请求不属于宏任务,js线程遇到ajax请求,会将请求交给对应的http线程处理,一旦请求返回结果,就会将对应的回调放入宏任务队列,等请求完成执行。)
- 微任务:Promise.then(非new Promise),process.nextTick(node中)
事件循环的执行机制
promise、async/await
- 首先,new Promise是同步的任务,会被放到主线程中立即执行,而Promise.then是异步任务,会被推到微任务中;
- 带async关键字的函数会返回一个promise对象,如果里面没有await,就等同于普通函数;
- await关键字必须在async函数内部使用,阻塞其后面的代码的执行,await之后的代码会被推到微任务队列中,等async外面的同步代码执行完毕再返回执行await下面的代码;
下面看一个面试题
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");
整理流程如下:
- 执行 console.log('script start'),输出script start;
- 遇到 setTimeout,是一个异步动作,放入宏任务队列中;
- 执行同步async1(),输出async1 start,遇到await,先得到await右侧表达式的结果,执行async2(),输出async2,并且return Promise.resolve(undefined),然后中断async函数,推到微任务队列中;
- 执行同步new Promise,输出promise1,遇到 promise.then()推入微任务队列;
- 执行同步console.log('script end'),输出script end;
- 到此同步的代码都执行完毕,再去微任务队列中依次执行微任务;
- 回到async内部,执行await Promise.resolve(undefined),待promise的状态是fulfilled,输出了async1 end;
- 然后执行Promise.then(),输出了promise2;
- 开启宏任务setTimeout,输出了settimeout;
个人理解
- 一段代码被执行,直接开启一个宏任务,会先执行宏任务中的同步代码(async、Promise),发现promise.then()之类微任务时,将其推到当前宏任务的微任务队列中,等本轮宏任务的同步代码执行完毕,再依次执行当前宏任务中的微任务;
- 在发现setTimeout之类的宏任务时将其推到宏任务队列中,等到第一轮宏任务中的所有任务都执行完毕,开启第二轮的宏任务;
- 循环往复,直至队列中不再有可执行任务,over!