专栏首页web前端技术分享JS中的同步异步编程,宏任务与微任务的执行顺序

JS中的同步异步编程,宏任务与微任务的执行顺序

首先我们先看看同步与异步的定义,及浏览器的执行机制,方便我们更好地理解同步异步编程。

  浏览器是多线程的,JS是单线程的(浏览器只分配一个线程来执行JS)

  进程大线程小:一个进程中包含多个线程,例如在浏览器中打开一个HTML页面就占用了一个进程,加载页面的时候,浏览器分配一个线程去计算DOM树,分配其它的线程去加载对应的资源文件...再分配一个线程去自上而下执行JS

  同步:在一个线程上(主栈/主任务队列)同一个时间只能做一件事情,当前事情完成才能进行下一个事情(先把一个任务进栈执行,执行完成,在把下一个任务进栈,上一个任务出栈...)

  异步:在主栈中执行一个任务,但是发现这个任务是一个异步的操作,我们会把它移除主栈,放到等待任务队列中(此时浏览器会分配其它线程监听异步任务是否到达指定的执行时间),如果主栈执行完成,监听者会把到达时间的异步任务重新放到主栈中执行...

  [宏任务:macro task]

- 定时器

- 事件绑定

- ajax

- 回调函数

- Node中fs可以进行异步的I/O操作

  [微任务:micro task]

- Promise(async/await) => Promise并不是完全的同步,当在Excutor中执行resolve或者reject的时候,此时是异步操作,会先执行then/catch等,当主栈完成后,才会再去调用resolve/reject把存放的方法执行

- process.nextTick (node中实现的api,把当前任务放到主栈最后执行,当主栈执行完,先执行nextTick,再到等待队列中找)

   - MutationObserver (创建并返回一个新的 MutationObserver 它会在指定的DOM发生变化时被调用。)

  执行顺序优先级:SYNC => MICRO => MACRO

所有JS中的异步编程仅仅是根据某些机制来管控任务的执行顺序,不存在同时执行两个任务这一说法

先来看一个例子:

setTimeout(() => {
    console.log(1);
}, 20);

setTimeout(() => {
    console.log(2);
}, 0);//=>默认会有最小的等待时间(V8一般是5~6MS)

console.time('WHILE');
let i = 0;
while (i <= 99999999) {
    i++;
}
console.timeEnd('WHILE');

setTimeout(() => {
    console.log(3);
}, 10);

console.log(4);

结果输出如图:

我们先模拟下浏览器的程序执行过程,代码自上而下执行,碰到第一个程序,先放入主栈(主任务队列),此时浏览器发现这是一个宏任务定时器,把它移出主栈,放入等待任务队列,再继续执行下面的代码,放入主栈执行,发现第二个任务也是宏任务的定时器,放入等待队列,继续往下执行,推入主栈,同步任务,循环99999999次之后输出次数,再执行下一个程序,也移入等待队列,再执行代码,发现是同步任务,输出4,此时主栈空闲,任务队列到达时间后先进先出的原则,首先第二个任务到达时间,把它放入主栈执行,输出2,此时本因输出3,因为第三个程序是10ms到达,第一个是20s到达,但是第三个程序是等待247.849853515625ms后才放入的等待队列,所以第一个程序先到达,输出1,最后输出3。

我们用ajax来看看js的同步与异步的执行顺序和机制,AJAX任务开始:SEND,AJAX任务结束:状态为4

let xhr = new XMLHttpRequest();
xhr.open('GET', 'xxx.txt', false);
// 放到等待区的时候,此时状态是1
xhr.onreadystatechange = () => { 
    console.log(xhr.readyState);//=>4
};
xhr.send();
// 同步ajax,xhr.send时为同步,xhr.send()执行完后状态为4,任务状态为4的时候主栈空闲,onreadystatechange监听到状态变化,输出4
 let xhr = new XMLHttpRequest();
 xhr.open('GET', 'xxx.txt', false);
 xhr.send();
// 状态已经为4了
 xhr.onreadystatechange = () => {//=>状态改变才会触发,放到等待区的时候状态已经为4了,不会在改变了,所以不会执行这个方法(啥都不会输出)
     console.log(xhr.readyState);
 };
 let xhr = new XMLHttpRequest();
 xhr.open('GET', 'xxx.txt');
 xhr.send();//=>异步操作:执行SEND后,有一个线程是去请求数据,主栈会空闲下来
// 放等待区之前状态是1
 xhr.onreadystatechange = () => {
     console.log(xhr.readyState);//=> 2 3 4
 };
// 主栈又空闲了
// 状态为2  把函数执行
// 状态为3  把函数执行
// 状态为4  把函数执行

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • js判断对象是否为空对象的几种方法

    1.将json对象转化为json字符串,再判断该字符串是否为"{}" var data = {}; var b = (JSON.stringify(data) ...

    TimothyJia
  • Vue中computed和watch的区别

    3.computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过的数据通过计算得到的

    TimothyJia
  • DOM盒子模型常用属性client,offset和scroll

    [获取元素具体的某个样式值] 1.[元素].style.xxx 操作获取 只能获取所有写在元素行内上的样式(不写在行内上,不管你写没写都获取不到,真实项目...

    TimothyJia
  • 异步与回调/函数的作用域链

    程序里面所有的任务,可以分成两类:同步任务(synchronous)和异步任务(asynchronous)。

    代码之风
  • 如何添加windows定时计划任务?

    “ 我们常常使用linux添加定时任务,windows也可以吗?答案是肯定的——编程三分钟”

    编程三分钟
  • 如何解释Event Loop面试官才满意?

    想要了解JavaScript引擎,首先我们从它的运行机制Event Loop来说起。

    童欧巴
  • 面试题:说说事件循环机制(满分答案来了)

    JavaScript代码的执行过程中,除了依靠函数调用栈来搞定函数的执行顺序外,还依靠任务队列(task queue)来搞定另外一些代码的执行。整个执行过程,我...

    winty
  • JavaEE开发之Spring中的多线程编程以及任务定时器详解

    上篇博客我们详细的聊了Spring中的事件的发送和监听,也就是常说的广播或者通知一类的东西,详情请移步于《JavaEE开发之Spring中的事件发送与监听以及使...

    lizelu
  • Spring 异步调用,一行代码实现!舒服,不接受任何反驳~

    在日常开发中,我们的逻辑都是同步调用,顺序执行。在一些场景下,我们会希望异步调用,将和主线程关联度低的逻辑异步调用,以实现让主线程更快的执行完成,提升性能。例如...

    芋道源码
  • 如何让训练神经网络不无聊?试试迁移学习和多任务学习

    训练深度神经网络是一个乏味的过程。更实际的方法,如重新使用训练好的网络解决其他任务,或针对许多任务使用相同的网络。这篇文章中,我们会讨论两个重要的方法:迁移学习...

    AI研习社

扫码关注云+社区

领取腾讯云代金券