1)定义
首先, js 是单线程的 ,主要的任务是处理用户的交互,而用户的交互无非就是响应 DOM 的增删改,那如何处理事件响应呢?
浏览器的各种 Web API 会为异步的代码提供了一个单独的运行空间,当异步的代码运行完毕以后,会将代码中的回调送入到 Task Queue(任务队列)中去,等到调用栈空时,再将队列中的回调函数压入调用中执行,等到栈空以及任务队列也为空时,调用栈仍然会不断检测任务队列中是否有代码需要执行,这一过程就是完整的 Event loop 了。
同时需要注意的是,js 引擎在执行过程中有优先级之分, js 引擎在一次事件循环中, 会先执行 js 线程的主任务,然后会去查找是否有微任务 microtask(promise),如果有那就优先执行微任务,如果没有,再去查找宏任务 macrotask(setTimeout、setInterval) 进行执行。
2)执行栈
在代码运行过程中,主线程([JavaScript]引擎线程)中有一个执行栈,执行栈中,又存在两个东西,一个是堆,一个是栈,栈里面则是当前要执行的代码
-
堆: 一种存放复杂或者说引用类型的内存区域
-
动态分配的内存,大小不定,不会自动释放,存放引用类型,包含引用类型的变量,实际上保存的不是变量本身,而是指向该对象的指针(地址)。
-
栈:一种存放数据的内存区域,当前要执行的代码
-
先进先出,后进后出,一个有序的列表
流程图:
3)同步和异步
-
同步任务在执行栈中执行,而异步任务执行完毕后进入任务队列
-
当前所有同步任务执行完毕后,才会进入人物队列中执行等待中的异步任务
-
流程图:
- 同步任务由JavaScript主线程次序执行
- 异步任务委托给宿主环境执行
- 已完成的异步任务对应的回调函数,会被加入到任务队列中
- JavaScript主线程的执行栈被清空后,会读取任务队列中的回调函数,次序执行
- JavaScript主线程不断的重复上面的第4步
4) 常用的宏任务和微任务
1)常用的宏任务和微任务有:
| 名称 | 举例(常用) |
|---|---|
| 宏任务 | script、setTimeout 、setInterval 、setImmediate、I/O、UI Rendering |
| 微任务 | process.nextTick()、Promise |
上诉的 setTimeout 和 setInterval 等都是任务源,真正进入任务队列的是他们分发的任务。
2)优先级
setTimeout = setInterval 一个队列 setTimeout > setImmediate process.nextTick > Promise
for(const macroTask of macroTaskQueue){ // 2.再执行宏任务 handleMacroTask(); for(const microTask of microTaskQueue){ // 1.先执行微任务 handleMicroTask(); } }
- 优先级: 微任务 => DOM渲染 => 宏任务
- 每一个宏任务执行完之后,都会检查是否存在待执行的微任务,如果有,则执行完所有微任务,再继续执行下一个宏任务
- 你可以理解为宏任务和微任务都是任务队列,只是用来存放东西的队列。
- 然后呢主线程可以执行代码,渲染UI之类的。主线程空闲的时候就会去轮询看看宏任务队列有没有要执行的任务,没有就过,有就取出来执行。
- 宏任务执行完会去看微任务队列。没有就过,有就取出来执行。
(3)setTimeout(fn,0)多久才执行,Event loop?
setTimeout 按照顺序放到队列里面,然后等待函数调用栈清空之后才开始执行,而这些操作进入队列的顺序,则由设定的延迟时间来决定。
接下来看题解
setTimeout(() => {
console.log('setTimeout')
},0);
Promise.resolve().then(() => {
console.log('promise');
Promise.resolve().then(() => {
console.log('promise2');
})
})
console.log('main');`
**log(‘main’) log(‘promise’) log(‘promise2’) log(‘setTimeout’);**
`setTimeout(() => {
Promise.resolve().then(() => {
console.log('promise')
})
},0);
Promise.resolve().then(() => {
setTimeout(() => {
console.log('setTimeout')
},0)
});
console.log('main');`
**log(‘main’) log(‘promise’) log(‘setTimeout’);**
`async function async1 () {
console.log('async1 start');
await async2();
console.log('async end')
}
async function async2 () {
console.log('async2')
}
console.log('script start')
setTimeout(() => {
console.log('serTimeout')
}, 0)
async1()
new Promise((function (resolve) {
console.log('promise1')
resolve()
})).then(function () {
console.log('promise2')
}).then(function () {
console.log('promise3')
}).then(function () {
console.log('promise4')
})
console.log('script end')`
// 'script start'
// 'async1 start'
// 'async2'
// 'promise1'
// 'script end'
// 'async1 end'
// 'promise2' promise3 promise4
// "serTimeout"
// 1. 创建函数async1 创建函数async2
// 2. 执行主线程 打印 script start
// 3. 遇到setTime 放到宏任务
// 4. 执行async1 函数 打印 async1 start执行async2函数打印async2 放入微任务
// 5. 继续执行 new Promise是立即执行 打印peimise1 resolve是异步的 放到微任务
// 6. 往下执行 打印 script end
// 7. 主线程执行完毕 看看微任务(拿出来一个执行一个就是event loop) 先打印 async end 然后再看看再执行 promise2
// 8. 微任务执行完了执行宏任务 打印 settimeout