1. js 单线程
js 单线程是因为是浏览器脚本语言,需要用户操作,同时只能有个操作DOM的行为,如果设计成多线程,一个线程添加DOM节点,另一个线程删除DOM节点,这个时候浏览器就为难了,哪个是准确的操作,为了避免这种复杂性,js设计成了单线程
2.任务队列
但是单线程同时带来了问题,所有执行任务需要排队,必须前一个任务执行完,才会执行后一个任务,如果前一个任务很耗时,后一个任务就不得不等着,所以在设计的时候挂起等待中的任务,先运行后面的任务,等有返回结果的时候回调函数放入任务队列中再由主线程执行
同步任务:主线程依次执行任务,执行完前一个再执行下一个
异步任务:不进入主线程执行栈,而是进入 任务队列,待有返回结果之后待主线程执行异步任务的回调函数
3. 事件循环
- 如图所示 js 执行栈当栈里有同步任务时候一直执行
- 当执行栈中没有同步任务,就会调用任务队列当中的异步任务
- 任务队列当中存在宏任务和微任务,如果微任务存在,就执行微任务直到微任务队列清空,再去执行宏任务
- 每次单个宏任务执行完毕后,检查微任务队列是否为空,如果不为空就会重复 第3步骤,然后再执行下一个宏任务,如此循环
举一个例子
console.log('start')
setTimeout(function() {
console.log('setTimeout')
}, 0)
Promise.resolve()
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})
console.log('end')
第一次循环, 执行同步代码,宏任务和微任务进入到各自队列中
MacroTasks: run script. setTimeout callback
MicroTasks: Promise.then()
JS stack: script
log: start, end
第二次循环, 微任务队列中先执行promise1,然后promise.then()加入微任务队列,继续执行为微任务队列输出 promise 2,然后微任务队列为空
MacroTasks:setTimeout callback
MicroTasks: Promise2.then()
JS stack: Promise2.then()
log: start, end, promise1,promise2
第三次循环,微任务
MacroTasks: setTimeout callback
MicroTasks:
JS stack:
log: start, end, promise1, promise2, setTimeout
到此队列清空