浏览器 Eventloop
执行顺序
阮一峰老师谈Event loop
- 所有的同步任务都在主线程上执行,形成一个执行栈(execution context stack)
- 主线程之外,还有一个任务队列(task queue)
- 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,里面的异步任务就会结束等待状态,进入执行栈,开始执行
- 主线程会不断的重复上面的三部内容
个人理解
在一次事件轮询中,有一个主线程,形成一个执行栈,还有一个任务队列,和一个微任务队列。
代码在主线程当中,当遇到同步代码,就把它执行完;如果遇到微任务,就把它放入微任务队列中,当遇到异步宏任务时,就把它放入宏任务队列。当同步任务执行完后,会检查是否存在微任务,如果有就把微任务队列清空。至此,本次事件轮询结束。然后执行栈,去取任务队列里面的异步宏任务,执行里面的代码,重复以上操作....直到代码执行完毕
微任务: process.nextTick, promise, MutationObserver
宏任务: script, 同步代码属于宏任务
settimeout, setInterval, setImmediate, I/O, UI-rendering
执行栈
执行栈可以认为是一个存储函数调用的栈结构
进程,线程
进程:CPU在运行指令及加载和保存上下文所需要的时间
线程:线程是进程中更小的单位,描述了执行一段指令所需要的时间
JS是单线程语言
Js单线程带来什么好处 ?
节省内存,更安全的渲染
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2 start');
return new Promise((resolve, reject) => {
resolve();
console.log('async2 promise');
})
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0); // 去到宏任务队列
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() { // 微任务队列
console.log('promise2');
}).then(function() { // 微任务队列
console.log('promise3');
});
console.log('script end');
// script start, async1 start, async2 start ,async2 promise,promise1,script end,
//promise2,promise3,setTimeout
async await
在函数前声明async,意味着会返回一个promise对象。async函数内部return语句返回的值,会成为then方法回调函数的参数。也就是说,写了async就相当于是promise里面执行了resolve(),它代表的一定是成功的情况。当async内部有await时,await这条语句的内容会立即执行,但是await之后的代码会被阻塞。
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
(也就是指async2函数会被立即执行,但是后面的console.log('async1 end')会被阻塞,它会去到下一次事件轮询的微任务队列)
然后跳出当前函数,去执行外部的同步代码。当同步代码执行完,去查看当前异步任务中的微任务队列,清空微任务队列后,执行异步中的宏任务队列。至此一次事件轮询结束。再进入下一次事件轮询,里面有settimeout,去到异步宏任务,将再次去到下一次事件轮询,而这个console.log('async1 end')处于这次事件轮询的微任务队列,会被执行。(至于为什么会在这次事件轮询的微任务队列,是await导致的,它实际上是在promise.then()里面的,这里涉及到generator的用法),然后再次去到下一次事件轮询,执行setTimeout里面的内容。