作为一个菜鸟,至今才弄明白前端async、await以及setTimeout的执行顺序

问题由来

前端JS异步处理,多个异步如何避免“瀑布编程”?如何使用async和await?在延迟处理setTimeout中如何确定执行顺序?

对此引出前端任务执行中任务队列的概念:宏任务、微任务以及主线程。

大佬请移驾

直接上代码:

async function async1() {
        console.log('async1 start');
        await async2();
        console.log('async1 end');
      }

      async function async2() {
        console.log('async2');
      }

      console.log('script start');
      setTimeout(function () {
        console.log('setTimeout');
      }, 0);

      async1();

      new Promise(function (resolve) {
        console.log('promise1');
        resolve();
      }).then(function () {
        console.log('promise2');
      });

      console.log('script end');

首先我们需要明白以下几件事情:

JS分为同步任务和异步任务
同步任务都在主线程上执行,形成一个执行栈
主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。

下面解释一下宏任务和微任务:

宏任务:macrotask(又称之为宏任务),可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。浏览器为了能够使得 JS 内部 macrotask 与 DOM 任务能够有序的执行,会在一个 macrotask 执行结束后,在下一个 macrotask 执行开始前,对页面进行重新渲染。

macrotask 主要包含:script(整体代码)、setTimeout、setInterval、I/O、UI 交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)

微任务:microtask(又称为微任务),可以理解是在当前 task 执行结束后立即执行的任务
。也就是说,在当前 task 任务后,下一个 task 之前,在渲染之前。所以它的响应速度相比setTimeout(setTimeout是task)会更快,因为无需等渲染。也就是说,在某一个 macrotask 执行完后,就会将在它执行期间产生的所有 microtask 都执行完毕(在渲染前)。microtask 主要包含:Promise.then、MutaionObserver、process.nextTick(Node.js 环境)

请给出以上代码执行结果:

说实话 之前遇到这种问题直接就是懵逼的,只知道async异步操作,setTimeout延迟执行,但是延迟0秒我都没有遇到和试过。

上结果:
在这里插入图片描述

如果明白此结果的大佬可以互相的讨论讨论,我说的只是我自己的理解,下面根据我自己的理解来给出执行流程。

  • 都知道的JS执行顺序,主线程从上往下依次执行首先遇到打印console.log('script start'),方法未调用不执行这个不用多说哈,继续往下执行;
  • 然后遇到setTimeout,由于它是个宏任务,则先进入宏任务队列中,继续执行;
  • 调用方法async1,由于方法立即执行则执行console.log('async1 start'),然后方法内继续执行 await async2();因为遇到await,await会让下面的内容进入微任务中,则将console.log('async1 end')放入微任务队列中,而await还是会立即执行,则执行方法async2(),方法中立即调用console.log('async2');;则当前线程继续往下执行;
  • 遇到new Promise对象,对象内容立即执行console.log('promise1');,而对象.then是一个微任务,则将内容console.log('promise2');放入微任务中,线程继续往下;
  • 最后遇到 console.log('script end');,则继续打印。当前线程执行结束。
  • 当前线程结束打印结果为:script start;async1 start;async2;promise1;script end这是线程执行结束的结果,由于微任务和宏任务中还没有执行结束,所以继续执行微任务中的任务;继续依次打印微任务中的数据:async1 end;promise2;当微任务执行结束后继续执行宏任务:setTimeout
  • 最后的结果:script start;async1 start;async2;promise1;script end;async1 end;promise2;setTimeout

下面图片解释执行过程
在这里插入图片描述其中数字代表线程执行遇到的顺序,主线程第一次执行结束后,再次执行微任务队列中的任务,最后执行的是宏任务队列中的任务。

下面再通过一个例子去更深次理解宏任务和微任务。

PS:

 async function async1() {
        console.log('async1 start');
        await async2();
        console.log('async1 end');
      }
      async function async2() {
        new Promise(function(resolve) {
          console.log('promise1');
          resolve();
        }).then(function() {
          console.log('promise2');
        });
      }
      console.log('script start');
      setTimeout(function() {
        console.log('setTimeout');
        }, 0)
      async1();
      new Promise(function(resolve) {
        console.log('promise3');
        resolve();
      }).then(function() {
        console.log('promise4');
      });
      console.log('script end');

以上例子在基础上做了小小的改动,直接上图片解释结果:
在这里插入图片描述
所以他的结果是:
在这里插入图片描述

对不起 ~ 我只是一个菜鸟,如果有解释不清或者有问题的请使劲喷我!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值