JavaScript中的事件循环机制及其运行原理
作者:anjushi_
javascript是单线程的非阻塞的脚本语言
单线程
只有一个主线程来处理任务。
非阻塞
JS引擎执行异步任务时,不会一直等待返回结果,主线程会挂起(pending)这个任务,继续执行其他任务,当异步任务返回结果时,js将异步任务的callback放到任务队列中,等到当前任务栈中的任务都执行完毕,处于闲置状态的主线程按照队列顺序将队首的calback函数加入到执行栈中,执行该函数的同步代码,如果又遇到异步任务,再将其回调函数加入到队列中–事件循环机制。
JS通常是非阻塞的,除了某些特殊情况,JS会停止代码执行:alert, confirm, prompt
js的任务队列分为同步任务和异步任务
同步任务
在主线程里执行,当浏览器第一遍过滤html文件的时候可以执行完;(在当前作用域直接执行的所有内容,包括执行的方法、new出来的对象)
异步任务
比较耗费时间与性能的,当浏览器执行到这些的时候会将其丢到异步任务队列中,不会立即执行的任务
异步任务分为宏任务(macrotask) 和 微任务(microtask),执行的优先级不同
宏任务:script, setTimeout, setInterval, setImmeditate, T/O, UI rendering
微任务:process, nextTick, promise.then(), object.observe, MutationObserver, await, async
回调函数是微任务,会被加入微任务队列,回调函数是宏任务,会被加入宏任务队列,微任务优先级高于宏任务
Event loop过程
- 主线程开始执行一段代码, 假设开始执行一个 script 标签内的代码,将代码放入执行栈中执行,同步代码优先执行,执行过程中,当遇到任务源时,判断是宏任务还是微任务。
- 如果是宏任务,加入到宏任务队列中,如果是微任务,加入到微任务队列中。
- 同步代码执行完成,执行栈空闲,检查微任务队列中是否有可执行任务,如果有,依次执行所有微任务队列中的任务。如果没有。当前任务执行结束。
- DOM渲染。
- 检查宏任务队列是否有可执行的宏任务,如果有,取出队列中最前面的那个宏任务,加入到执行栈中开始执行,然后重复前面步骤,直到宏任务队列中所有任务执行结束
微任务在DOM渲染前触发,宏任务在DOM渲染后触发
代码示例1
// 语句一 console.log(1); // 语句二 setTimeout(()=>{ console.log(2); },0); //语句三 Promise.resolve().then(()=>{ console.log(3); }) // 语句四 console.log(4);
//输出顺序
//1,4,3,2
代码示例2
console.log("script start"); setTimeout(function () { console.log("setTimeout"); }, 0); Promise.resolve() .then(function () { console.log("promise1"); }) .then(function () { console.log("promise2"); }); console.log("script end");
代码示例3
new Promise(…)中的代码,也是同步代码,会立即执行。只有then之后的代码,才是异步执行的代码
console.log("script start"); setTimeout(function () { console.log("timeout1"); }, 10); new Promise((resolve) => { console.log("promise1"); resolve(); setTimeout(() => console.log("timeout2"), 10); }).then(function () { console.log("then1"); }); console.log("script end");
代码示例4
async 和 await 是 Generator 和 Promise 的语法糖。async 函数和普通函数一样,只是表示这个函数里有异步操作的方法,并返回一个 Promise 对象
await后面的函数执行完毕时,await会产生一个微任务(Promise.then是微任务)。
// 等价 async function async1() { console.log("async1 start"); await async2(); console.log("async1 end"); } // Promise 写法 async function async1() { console.log("async1 start"); Promise.resolve(async2()).then(() => console.log("async1 end")); }
async function async1() { console.log("async1 start"); await async2(); console.log("async1 end"); } async function async2() { console.log("async2"); } async1(); setTimeout(() => { console.log("timeout"); }, 0); new Promise(function (resolve) { console.log("promise1"); resolve(); }).then(function () { console.log("promise2"); }); console.log("script end");
代码示例5
// 1. 开始执行 console.log(1) // 2. 打印 1 setTimeout(function () { // 6. 浏览器在 0ms 后,将该函数推入任务队列 console.log(2) // 7. 打印 2 Promise.resolve(1).then(function () { // 8. 将 resolve(1) 推入任务队列 9. 将 function函数推入任务队列 console.log('ok') // 10. 打印 ok }) }) // 3.调用 setTimeout 函数,并定义其完成后执行的回调函数 setTimeout(function () { // 11. 浏览器 0ms 后,将该函数推入任务队列 console.log(3) // 12. 打印 3 }) // 4. 调用 setTimeout 函数,并定义其完成后执行的回调函数 // 5. 主线程执行栈清空,开始读取 任务队列 中的任务 // output: 1 2 ok 3
代码示例6
// 1. 开始执行 console.log(1) // 2. 打印 1 setTimeout(function () { // 6. 浏览器在 0ms 后,将该函数推入任务队列 console.log(2) // 7. 打印 2 Promise.resolve(1).then(function () { // 8. 将 resolve(1) 推入任务队列 9. 将 function函数推入任务队列 console.log('ok') // 10. 打印 ok }) }) // 3.调用 setTimeout 函数,并定义其完成后执行的回调函数 setTimeout(function () { // 11. 浏览器 0ms 后,将该函数推入任务队列 console.log(3) // 12. 打印 3 }) // 4. 调用 setTimeout 函数,并定义其完成后执行的回调函数 // 5. 主线程执行栈清空,开始读取 任务队列 中的任务 // output: 1 2 ok 3
代码示例7
setTimeout(function(){ console.log('1') }); new Promise(function(resolve){ console.log('2'); resolve(); }).then(function(){ console.log('3') }); var timer; timer = setInterval(function(){ console.log('5'); clearInterval(timer); }); new Promise(function(resolve){ resolve(); }).then(function(){ console.log('6') }); console.log('4'); // 2,4,3,6,1,5
代码示例8
Promise.resolve().then(()=>{ console.log('Promise1') setTimeout(()=>{ console.log('setTimeout2') },0) }); setTimeout(()=>{ console.log('setTimeout1') Promise.resolve().then(()=>{ console.log('Promise2') }) },0); console.log('start'); // start -> Promise1 -> setTimeout1 -> Promise2 - > setTimeout2
实例
<body> <div id="box"></div> <script src="./app.js"></script>s </body> //js const box = document.getElementById('box') box.innerHTML = '<P>我是后来插进去的内容</P>' console.log("1"); setTimeout( ()=>{ console.log("2"); alert("定时器执行了") },0 ) Promise.resolve().then(()=>{ console.log("3") alert("Promise执行了") }) console.log("4");
tip:从规范来看,microtask (微任务)优先于 macrotask(宏任务) 执行,所以如果有需要优先执行的逻辑,放入microtask 队列会比 task 更早的被执行。
到此这篇关于JavaScript中的事件循环机制及其运行原理的文章就介绍到这了,更多相关JavaScript事件循环机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!