js之事件循环

JavaScript的事件循环是它的并发模型的核心部分,使得JavaScript能够在单线程中处理异步操作。事件循环允许JavaScript在执行代码时,同时进行非阻塞的I/O操作(如网络请求、文件操作等)。这个概念对于理解如何高效地构建交互式Web应用程序是至关重要的。

事件循环的工作流程

JavaScript运行时环境包含一个待处理消息的队列。每个消息都关联着一个用以处理这个消息的函数。当堆栈(包含当前执行的所有代码)为空时,事件循环会从队列中取出一个消息,然后处理它。这个处理包括调用与消息关联的函数(及其调用堆栈)。这个函数执行完毕后,堆栈空出,事件循环继续处理下一个消息,这个过程持续不断。

宏任务(Macrotask)与微任务(Microtask)

事件循环中的任务可以分成两种:宏任务(如setTimeout、setInterval、I/O操作)和微任务(如Promise.then、MutationObserver)。两者的主要区别在于它们被执行的时机。

宏任务(Macrotask): 每次执行完一个宏任务,都会检查微任务队列是否有任务,如果有,则会先执行微任务队列中的所有任务,然后才会执行下一个宏任务。

微任务(Microtask): 当前任务执行完毕后立即执行的任务。所有微任务执行完毕之后,若存在渲染操作,则会执行渲染操作,然后继续回到宏任务的执行。

事件循环的步骤

  1. 执行全局脚本:加载脚本后,全局脚本开始执行。
  2. 执行宏任务:一旦全局脚本执行完毕,事件循环会从宏任务队列中取出一个任务执行。
  3. 执行微任务:在当前宏任务执行完毕后,会检查微任务队列,如果队列不为空,则执行队列中的所有微任务。这些微任务可能会继续添加新的微任务到微任务队列中。事件循环会一直执行微任务队列中的任务,直到队列清空。
  4. 渲染:完成宏任务和微任务后,渲染界面(如果有必要的话)。
  5. 回到步骤2:继续从宏任务队列中取出下一个任务,重复这个过程。

例子1

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');

执行顺序:

console.log('script start');
  1. ‘script start’ 打印到控制台。这是同步代码的一部分,所以它首先执行。
setTimeout(function() {
    console.log('setTimeout');
}, 0);
  1. setTimeout是一个宏任务。尽管延迟是0,但这个setTimeout回调(任务)不会立即执行;它被加入到宏任务队列等待执行。在当前执行栈(包括紧接着执行的微任务)执行完之前,setTimeout的回调不会被执行。
Promise.resolve().then(function() {
    console.log('promise1');
}).then(function() {
    console.log('promise2');
});
  1. Promise.resolve()是立即解决的Promise,.then回调(设置的第一个promise1打印操作)被加入到微任务队列,将在当前宏任务的同步任务执行完毕后立即执行。

  2. 第二个.then(即promise2打印操作)也会成为一个微任务,它将在第一个.then执行后被加入到微任务队列。

console.log('script end');
  1. ‘script end’ 打印到控制台。这也是同步代码的一部分,紧随'script start'的打印之后执行。

至此,同步任务执行完毕。事件循环将会查看微任务队列,看是否有任务需要执行;在这种情况下,微任务队列中有两个由Promise产生的任务需要执行:

  1. ‘promise1’ 被打印到控制台。这是第一个微任务。

  2. 由于第一个.then的回调执行完毕,第二个.then的回调(作为微任务)现在执行,‘promise2’ 被打印到控制台作为第二个微任务。

至此,当前宏任务中的所有微任务都已经被执行。事件循环现在回转到宏任务队列,准备执行下一个宏任务:

  1. ‘setTimeout’ 的回调函数现在执行,因为它是在开始时通过setTimeout提交的宏任务。‘setTimeout’ 被打印到控制台。

最终的执行顺序是:

  1. ‘script start’
  2. ‘script end’
  3. ‘promise1’
  4. ‘promise2’
  5. ‘setTimeout’

例子2

console.log('1');

setTimeout(function () {
    console.log('2');
    process.nextTick(function () {
        console.log('3');
    })
    new Promise(function (resolve) {
        console.log('4');
        resolve();
    }).then(function () {
        console.log('5')
    })
})
process.nextTick(function () {
    console.log('6');
})
new Promise(function (resolve) {
    console.log('7');
    resolve();
}).then(function () {
    console.log('8')
})

setTimeout(function () {
    console.log('9');
    process.nextTick(function () {
        console.log('10');
    })
    new Promise(function (resolve) {
        console.log('11');
        resolve();
    }).then(function () {
        console.log('12')
    })
})
  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值