JS中的事件循环机制(Event Loop)

JS中的事件循环机制(Event Loop)

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

const p = Promise.resolve();

(async () => {
    await p;
    console.log('await end');
})();

p.then(() => {
    console.log('then 1');
}).then(() => {
    console.log('then 2');
});

// 'then 1' => 'then 2' => 'await end'

代码示例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 更早的被执行。
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

anjushi_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值