美文网首页
JS同步异步与事件循环

JS同步异步与事件循环

作者: 郝同学1208 | 来源:发表于2020-10-16 10:02 被阅读0次

文章序

理解JS的单线程和同步异步逻辑、事件循环(event loop)是学习JavaScript进阶的必经之路,因此,本文章旨在让你一文彻底弄懂JS的单线程,异步,事件循环.

单线程

众所周知,浏览器的运行是多进程的,比如每多打开一个tab页,在任务管理器中就可以看到给浏览器多开辟了一个进程。每一个浏览器进程对应着多个线程,包括GPUI渲染线程、JS引擎线程、事件触发线程、定时器触发线程、http请求线程。因为JS的运行是单线程的,即代码由上自下运行,所以如果遇到循环次数多,或者其他耗时比较长的代码,便会阻塞.

那么为什么JS是单线程的呢?举个很简单的例子,如果JS是多线程,在两个线程内同时对一个dom元素进行操作,那么,该dom元素该按照哪一个线程的要求进行渲染呢?这就是问题之所在。因此,JS选择了单线程.

JS的同步异步

就像刚刚说的那样,JS的运行是单线程的,如果出现比较耗时的代码,便会产生阻塞,直观体现就是用户的浏览器卡死、白屏等。因此,为了减少这种情况的出现,JS中出现了同步代码和异步代码.

同步
同步代码:即script标签内包裹的JS代码,包括通过script来import导入JS文件,也是属于同步代码,因此这里也就涉及到.js文件引入的优化问题,解决方案如AMD,CMD,ES6模块引入等,当然这就是另一个问题了.

异步
异步代码:典型的如Ajax请求,settimeout,setinterval等,我们知道为了防止浏览器卡死、白屏,JS引入了异步机制,那么同样的两段异步代码,先后执行顺序是怎样的?这里就要引出事件循环(Event loop)了.

事件循环

当代码/任务执行时,首先判断是同步任务还是异步任务,同步任务推入主线程,异步任务推入任务队列,主线程一直执行同步任务,直到所有同步任务执行完毕,主线程为空,则去任务队列中查看异步任务,将回调函数推入主线程执行,重复循环上述过程,就是事件循环.

看下代码:

setTimeout(()=>{
console.log("定时器开始执行");
})
 
new Promise( function (resolve) {
    console.log("准备执行for循环了");
    for(var i=0; i<100; i++) {};
    resolve();
}).then( () => console.log("执行then函数") );
 
console.log("代码执行完毕");

我们试着对执行结果分析:

setTimeout 是异步任务,被放到任务队列;
new Promise 是同步任务,被放到主进程里,直接执行打印 '马上执行for循环啦';
.then里的函数是 异步任务,被放到任务队列;
 console.log('代码执行结束')是同步代码,被放到主进程里,直接执行;

所以最后的执行结果是:[准备执行for循环---代码执行完毕---定时器开始执行---执行then函数 ],对吗?

可是执行结果却是:[准备执行for循环---代码执行完毕---执行then函数---定时器开始执行],为什么会是这样呢?难道是异步任务执行的顺序不是前后顺序,而是另有规定?

事实上,按照异步同步的划分并不准确而准确的方式应该是:
macro-task(宏任务):script、setTimeout、setInterval、I/O、UI 交互事件、setImmediate(Node.js);
micro-task(微任务):Promise、MutaionObserver、process.nextTic(Node.js);

事件循环

按照上图这种分类方式,JS 的执行机制是:
1、执行一个宏任务,过程中如果遇到微任务,就将其放到微任务的[事件队列]里;
2、当前宏任务执行完成后,会查看微任务的[事件队列],并将里面的微任务按先进先出依次执行完;
重复以上2步骤,结合事件循环就是更为准确的JS执行机制了;

按照这种执行机制,去分析第二段代码:

setTimeout(()=>{
console.log("定时器开始执行");
}, 0)

new Promise( function (resolve) {
    console.log("准备执行for循环了");
    for(var i=0; i<100; i++) {};
    resolve();
}).then( () => console.log("执行then函数") );
console.log("代码执行完毕");

首先执行 script 下的宏任务,遇到 setTimeout 将其放到宏任务的队列里
遇到 new Promise 立即执行,打印 "准备执行for循环" 
遇到 then 方法,是微任务,将其放到微任务的队列里
打印 "代码执行完毕" 
本轮宏任务执行完毕,查看本轮的微任务,发现有一个 then 方法里的函数, 打印 "执行then函数" 
到此,本轮的事件循环全部完成
下一轮的循环里,先执行宏任务,发现宏任务的队列里有一个 setTimeout 里的函数,执行打印 "定时器开始执行"
查看微任务队列,为空,结束本轮事件循环
查看宏任务队列,为空,线程结束

所以最后的执行顺序就是:[准备执行for循环-->代码执行完毕-->执行then函数-->定时器开始执行]

最后再说一下setTimeout
先看代码:

setTimeout(function(){
    console.log('执行了');
 },3000);

我们一般说这段代码,在3秒后执行setTimeout内的回调函数,但这种说法往往是不够严谨的,准确的解释是:3秒后,setTimeout函数会被推入事件队列,而事件队列内的任务,只有在主线程空闲时才会执行,所以,只有同时满足以下条件setTimeout内的函数才能被执行:
1、3秒后;
2、主线程空闲时;
若主线程的执行任务很多,执行时间超过3秒,比如说5秒,那么setTimeout内的函数只能在5秒后执行了.

相关文章

  • JS同步异步与事件循环

    文章序 理解JS的单线程和同步异步逻辑、事件循环(event loop)是学习JavaScript进阶的必经之路,...

  • 异步编程

    同步与异步 同步:按代码顺序依次执行 异步:先执行同步代码,完成后再执行异步代码 事件循环与消息队列:当代码执行到...

  • 夯基础- js event loop机制

    js运行机制 event loop事件循环 js分为同步任务和异步任务,所有的同步任务都在主线程上执行 另外存在着...

  • js笔记

    Javascript 事件循环: js解析方法时,将同步任务排队到执行栈中,异步任务排队到事件队列中。 事件队列分...

  • JavaScript 异步

    异步机制详解 才清晰知道浏览器里面的JS引擎运行来跑js,js中事件循环包含栈(用来执行同步任务),消息队列(用来...

  • ES6系列之Promise

    本篇目录 JS 同步与异步 常见异步处理回调函数事件监听Deferred对象 Promise对象概念 Promis...

  • Promise 解析和事件循环机制

    js单线程(线程中拥有唯一的一个事件循环) js分为同步任务和异步任务,同步任务都是在主线程上执行。当一个任务执行...

  • I/O模型与多路复用

    同步、异步、阻塞、非阻塞 同步 & 异步 同步与异步是针对多个事件(线程/进程)来说的。 如果事件A需要等待事件B...

  • js事件循环机制

    js事件循环机制 一、 执行栈 二、 任务队列(同步任务和异步任务) 三、 宏任务和微任务 四、 浏览器下的事件循...

  • 头条面试题:js同步异步代码执行顺序测试

    异步笔试题,请写出下面代码的运行结果: 这道题考的知识点: js事件循环机制 同步异步执行顺序 微任务宏任务 as...

网友评论

      本文标题:JS同步异步与事件循环

      本文链接:https://www.haomeiwen.com/subject/bmxzpktx.html