您现在的位置是:群英 > 开发技术 > web开发
新手怎样掌握JS中事件循环机制
Admin发表于 2022-05-26 17:20:13717 次浏览
今天这篇给大家分享的知识是“新手怎样掌握JS中事件循环机制”,小编觉得挺不错的,对大家学习或是工作可能会有所帮助,对此分享发大家做个参考,希望这篇“新手怎样掌握JS中事件循环机制”文章能帮助大家解决问题。

前言

我们知道javascript 是单线程的编程语言,只能同一时间内做一件事,按顺序来处理事件,但是在遇到异步事件的时候,js线程并没有阻塞,还会继续执行,这又是为什么呢?本文来总结一下js 的事件循环机制。

1、javascript是单线程的

javascript 是一种单线程的编程语言,只有一个调用栈,决定了它在同一时间只能做一件事。在代码执行的时候,通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行。在执行同步代码的时候,如果遇到了异步事件,js 引擎并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。因此js又是一个非阻塞、异步、并发式的编程语言。

2、同步和异步

同步和异步的关系就类似于我们在餐厅排队吃饭的时候,每个人必须挨个的排队来进行买饭这个操作,而在这个过程中十分无聊,这时候我们可以边排着队边玩下手机,不需多久就排到了我们买饭。这个排队过程就是js中的一个同步操作,玩手机就像一个异步操作。同步和异步的差别就在于排队买饭和玩手机这两个任务的执行顺序的不同。

同步: 指的是在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。可以理解为在执行完一个函数或方法之后,一直等待系统返回值或消息,这时程序是处于阻塞的,只有接收到返回的值或消息后才往下执行其他的命令。

异步: 指的是不进入主线程,某个异步任务可以执行了,该任务才会进入主线程执行。执行完函数或方法后,不必阻塞性地等待返回值或消息,只需要向系统委托一个异步过程,那么当系统接收到返回值或消息时,系统会自动触发委托的异步过程,从而完成一个完整的流程。

console.log(1);
settimeout(() => {
  console.log(2);
}, 0);
settimeout(() => {
  console.log(3);
}, 0);
settimeout(() => {
  console.log(4);
}, 0);
console.log(5);

上面的代码会打印  1 》 5 》 2 》 3 》4,为什么会产生这样的结果,我们来看下事件循环。

3、事件循环

事件循环过程可以简单描述为:

a、函数入栈,当 stack 中执行到异步任务的时候,就将他丢给 webapis ,接着执行同步任务,直到 stack 为空;

b、在此期间 webapis 完成这个事件,把回调函数放入 callbackqueue (任务队列)中等待;

c、当执行栈为空时,event loop 把 callback queue中的一个任务放入stack中,回到第1步。

事件循环(event loop) 是让 javascript 做到既是单线程,又绝对不会阻塞的核心机制,也是 javascript 并发模型(concurrency model)的基础,是用来协调各种事件、用户交互、脚本执行、ui 渲染、网络请求等的一种机制。在执行和协调各种任务时,event loop 会维护自己的事件队列。

事件队列是一个存储着待执行任务的队列,其中的任务严格按照时间先后顺序执行,排在队头的任务将会率先执行,而排在队尾的任务会最后执行。事件队列每次仅执行一个任务,在该任务执行完毕之后,再执行下一个任务,一个任务开始后直至结束,不会被其他任务中断。执行栈则是一个类似于函数调用栈的运行容器,当执行栈为空时,js 引擎便检查事件队列,如果不为空的话,事件队列便将第一个任务压入执行栈中运行。

任务队列:在javascript中,异步任务被分为两种,一种宏任务(macrotask)也叫task,一种叫微任务:

宏任务的例子很多,包括创建主文档对象、解析html、执行主线(或全局)javascript代码,更改当前url以及各种事件,如页面加载、输入、网络事件和定时器事件。从浏览器的角度来看,宏任务代表一个个离散的、独立工作单元。运行完任务后,浏览器可以继续其他调度,如重新渲染页面的ui或执行垃圾回收。

而微任务是更小的任务。微任务更新应用程序的状态,但必须在浏览器任务继续执行其他任务之前执行,浏览器任务包括重新渲染页面的ui。微任务的案例包括promise回调函数、dom发生变化等。微任务需要尽可能快地、通过异步方式执行,同时不能产生全新的微任务。微任务使得我们能够在重新渲染ui之前执行指定的行为,避免不必要的ui重绘,ui重绘会使应用程序的状态不连续。

当当前执行栈中的事件执行完毕后,js 引擎首先会判断微任务对列中是否有任务可以执行,如果有就将微任务队首的事件压入栈中执行。当微任务对列中的任务都执行完成后再去判断宏任务对列中的任务。每次宏任务执行完毕,都会去判断微任务队列是否产生新任务,若存在就优先执行微任务,否则按序执行宏任务。

事件循环通常至少需要两个任务队列:宏任务队列和微任务队列。两种队列在同一时刻都只执行一个任务。

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

按照上面的内容,分析执行步骤:

1、宏任务:执行整体代码(相当于<script>中的代码):

  • 输出: script start
  • 遇到 settimeout,加入宏任务队列,当前宏任务队列(settimeout)
  • 遇到 promise,加入微任务,当前微任务队列(promise1)
  • 输出:script end

2、微任务:执行微任务队列(promise1)

  • 输出:promise1,then 之后产生一个微任务,加入微任务队列,当前微任务队列(promise2)
  • 执行 then,输出promise2
  • 执行渲染操作,更新界面。
  • 宏任务:执行 settimeout
  • 输出:settimeout

注意: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");

步骤解析:

当前任务队列:微任务: [], 宏任务:[<script>]

宏任务:

  • 输出: script start
  • 遇到 timeout1,加入宏任务
  • 遇到 promise,输出promise1,直接 resolve,将 then 加入微任务,遇到 timeout2,加入宏任务。
  • 输出script end
  • 宏任务第一个执行结束
  • 当前任务队列:微任务[then1],宏任务[timeou1, timeout2]

微任务:

  • 执行 then1,输出then1
  • 微任务队列清空
  • 当前任务队列:微任务[],宏任务[timeou1, timeout2]

宏任务:

  • 输出timeout1
  • 输出timeout2
  • 当前任务队列:微任务[],宏任务[timeou2]

微任务:

  • 为空跳过
  • 当前任务队列:微任务[],宏任务[timeou2]

宏任务:

输出timeout2

注意:async 和 await 其实就是 generator 和 promise 的语法糖。async 函数和普通 函数没有什么不同,他只是表示这个函数里有异步操作的方法,并返回一个 promise 对象

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

步骤解析:

当前任务队列:宏任务:[<script>],微任务: []

宏任务:

  • 输出:async1 start
  • 遇到 async2,输出:async2,并将 then(async1 end)加入微任务
  • 遇到 settimeout,加入宏任务。
  • 遇到 promise,输出:promise1,直接 resolve,将 then(promise2)加入微任务
  • 输出:script end
  • 当前任务队列:微任务[promise2, async1 end],宏任务[timeout]

微任务:

  • 输出:promise2
  • promise2 出队
  • 输出:async1 end
  • async1 end 出队
  • 微任务队列清空
  • 当前任务队列:微任务[],宏任务[timeout]

宏任务:

  • 输出:timeout
  • timeout 出队,宏任务清空



以上就是关于“新手怎样掌握JS中事件循环机制”的介绍了,感谢各位的阅读,希望文本对大家有所帮助。如果想要了解更多知识,欢迎关注群英网络,小编每天都会为大家更新不同的知识。

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。

相关信息推荐
2022-06-16 17:06:14 
摘要:golang调用函数的方法:使用一个map变量显式的把字符串和函数关联起来,并通过【funcs["foo"]】得到函数对象,代码为【funcs := map[string]interface{} "foo": foo】。
2022-11-29 09:53:44 
摘要:php实现插件功能:1、新建函数文件“function.php”,代码内容是“function addAction($hook, $actionFunc){...}”;2、确认插件的Hook点,设置钩子埋入点;3、建立“check_all.php”和“login.php”两个文件,然后在“check_all.php”文件中写入“addAction(...)”即可。
2022-06-27 17:33:29 
摘要:bootstrap如何获取行数据?下面本篇文章给大家介绍一下。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。
云活动
推荐内容
热门关键词
热门信息
群英网络助力开启安全的云计算之旅
立即注册,领取新人大礼包
  • 联系我们
  • 24小时售后:4006784567
  • 24小时TEL :0668-2555666
  • 售前咨询TEL:400-678-4567

  • 官方微信

    官方微信
Copyright  ©  QY  Network  Company  Ltd. All  Rights  Reserved. 2003-2019  群英网络  版权所有   茂名市群英网络有限公司
增值电信经营许可证 : B1.B2-20140078   粤ICP备09006778号
免费拨打  400-678-4567
免费拨打  400-678-4567 免费拨打 400-678-4567 或 0668-2555555
微信公众号
返回顶部
返回顶部 返回顶部