JavaScript 的执行机制

本文深入探讨了JavaScript的单线程执行原理,解释了事件循环机制如何协调同步与异步任务,以及宏任务和微任务的区别。通过具体代码示例,分析了不同任务类型在执行队列中的调度顺序。

前言

console.log('代码执行开始...');

setTimeout(() => console.log('定时器开始了...'));

new Promise((resolve, reject) => {
    console.log('Promise开始了...');

    for (let i = 0; i < 10; i++) {
        i === 9 && resolve();
    }
}).then(() => console.log('then开始了...'));

console.log('代码执行结束');
// outputs:
// 代码执行开始...
// Promise开始了...
// 代码执行结束
// then开始了...
// 定时器开始了...

JavaScript 是一门单线程的语言,虽然在 H5 中提出了 Web-Worker,但是 JavaScript 是单线程语言这一核心仍然没有改变,但是单线程并不意味着 JavaScript 的执行顺序取决于语句出现的顺序

JavaScript 事件循环

这个时候我们理所当然地会想到同步任务和异步任务,有了同步任务和异步任务,我们就可以导出下面的导图:

在这里插入图片描述

  1. 同步任务和异步任务分别进入不同的执行队列,同步任务进入主线程,异步任务进入 Event Table
  2. 当指定的操作完成时,Event Table 会将其回调函数注册到 Event Queue
  3. 主线程的同步任务全部执行完毕之后,去 Event Queue 读取对应的回调函数,将其加入主线程,执行
  4. 上述3个步骤会不断重复,构成了 Event Loop

JavaScript 引擎中的 monitoring process 进程会执行检查主线程执行栈是否为空,如果为空,就回去 Event Queue 中检查是否存在等待被执行的函数,并将其加入到主线程执行栈,这个操作

setTimeout

在大致了解了 JavaScript 的执行机制之后,对于 setTimeout 函数我们应该有一些新的认值

setTimeout(() => console.log('this is setTimeout function...'), 1000);
sleep(5000); // JavaScript 中并没有封装好的 sleep 函数,这里是举例

很明显,控制台并不会在 1s 后打印 ‘this is setTimeout function…’,而是会在 5s 后打印,回到JavaScript的执行机制,我们会发现其实上述代码是这样执行的:

  1. setTimeout 进入 Event Table,开始计时
  2. 主线程执行 sleep 函数,这很慢,比 setTimeout 函数要慢
  3. 1s 到了,setTimeout 计时完成,其中的箭头函数加入到 Event Queue ,但是 sleep 函数还在执行,等待
  4. 5s 到了,sleep 函数执行完毕,箭头函数从 Event Queue 中加入到了主线程,开始执行

如果 setTimeout 函数中指定的时间是 0 的话,同理,其也不会立刻执行,而是等待主线程执行栈中没有任务,再加入到主线程执行,需要注意的是,即使主线程执行栈为空,0ms 也是达不到的,最低的时间是 4ms

但问题并没有彻底解决,观察第一个代码块:

console.log('代码执行开始...');

setTimeout(() => console.log('定时器开始了...'));

new Promise((resolve, reject) => {
    console.log('Promise开始了...');

    for (let i = 0; i < 10; i++) {
        i === 9 && resolve();
    }
}).then(() => console.log('then开始了...'));

console.log('代码执行结束');

setTImeout 函数中的回调函数和我们新建的 Promise 中的 resolve 函数明显是两个异步任务,而且 setTimeout 函数语句在 resolve 函数语句之前,为何 resolve 函数会比 setTimeout 函数中的回调函数先执行呢?这时,我们需要到导入宏任务和微任务

宏任务和微任务

  • 宏任务:整体代码、setTimeout、setInterval、IO、setImmediate、requestAnimationFrame、…
  • 微任务:Promise.then catch finally、process.nextTick、MutationObserver、…

不同类型的任务会进入不同的 Event Queue,事件循环的顺序会决定 JavaScript 代码的执行顺序,进入整体代码(宏任务)之后,开始第一次 Event Loop,当前次 Event Loop 中的宏任务执行完毕之后,开始执行微任务,当前次 Event Loop 中的微任务执行完毕之后,从 Event Queue 中拉取一个新的宏任务,开始第二次 Event Loop

回到第一个代码块:

console.log('代码执行开始...');

setTimeout(() => console.log('定时器开始了...'));

new Promise((resolve, reject) => {
    console.log('Promise开始了...');

    for (let i = 0; i < 10; i++) {
        i === 9 && resolve();
    }
}).then(() => console.log('then开始了...'));

console.log('代码执行结束');
  1. 整体代码为宏任务,进入主线程执行栈,console.log(‘代码执行开始…’)
  2. 遇到 setTimeout 函数,异步宏任务,将其加入宏任务 Event Table,当指定的操作完成时,会将其回到函数加入到 Event Queue
  3. 遇到 Promise,new Promise 立即执行,console.log(‘Promise开始了…’)
  4. 遇到 resolve 函数,异步微任务,将其加入微任务 EventTable,当指定操作完成时,将其回调函数也就是 then 函数加入 Event Queue
  5. 遇到 console.log(‘代码执行结束’),当前 Event Loop 中所有宏任务执行完毕
  6. 执行当前 Event Loop 中的微任务,console.log(‘then开始了…’)
  7. 当前 Event Loop 为空,开始下一个 宏任务,console.log(‘定时器开始了…’)

完整的 JavaScript 执行机制如下:
在这里插入图片描述

后记

请分析以下代码,作为最后的练习:

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');
    });
});
呼叫中心项目,基于 asterisk 开发的一套 web 项目。主要功能有自动外呼(催缴外呼、调查外呼、通知类外呼),催缴外呼支持几大业 务的外呼:电费催缴、水费催缴、电话费催缴、物业费催缴、交通移车、交通违法通知等八种催缴(提醒)类型。 同时支持扩展开发, 可以应用于各种电话….zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值