当浏览器或者Node拿到一段代码时首先做的就是传递给JavaScript引擎,并且要求它去执行。

然而,执行 JavaScript 并非一锤子买卖,宿主环境当遇到一些事件时,会继续把一段代码传递给 JavaScript 引擎去执行,此外,我们可能还会提供 API 给 JavaScript 引擎,比如 setTimeout 这样的 API,它会允许 JavaScript 在特定的时机执行。

所以,我们首先应该形成一个感性的认知:一个 JavaScript 引擎会常驻于内存中,它等待着我们(宿主)把 JavaScript 代码或者函数传递给它执行。

由于我们这里讲的是JavaScript 语言,我们把宿主发起的任务称为宏观任务,把 JavaScript 引擎发起的任务称为微观任务。

宏观和微观任务

JavaScript 引擎等待宿主环境分配宏观任务,在操作系统中,通常等待的行为都是一个事件循环,所以在 Node 术语中,也会把这个部分称为事件循环。我们用伪代码来表示,大概就是:

          while (TRUE) {

            r = wait();

            execute(r);

        }

我们可以看到,整个循环做的事情基本上就是反复“等待 - 执行”。当然,实际的代码中并没有这么简单,还有要判断循环是否结束、宏观任务队列等逻辑。

有了宏观任务和微观任务机制,我们就可以实现 JS 引擎级和宿主级的任务了,例如:Promise 永远在队列尾部添加微观任务。setTimeout 等宿主 API,则会添加宏观任务。在执行完一个宏观任务后再执行后一个宏观任务。

接下来我们介绍一下 Promise。

Promise 是 JavaScript 语言提供的一种标准化的异步管理方式,它的总体思想是,需要进行 io、等待或者其它异步操作的函数,不返回真实结果,而返回一个“承诺”,函数的调用方可以在合适的时机,选择等待这个承诺兑现(通过 Promise 的 then 方法的回调)。(建议不是很了解promise的可以去看一下阮一峰老师的ES6标准入门)

Promise 的 then 回调是一个异步的执行过程,下面我们就来研究一下 Promise 函数中的执行顺序,我们来看一段代码示例:

          var r = new Promise(function(resolve, reject){
            console.log("a");
            resolve()
            });
          r.then(() => console.log("c"));
          console.log("b")

我们执行这段代码后,注意输出的顺序是 a b c。在进入 console.log(“b”) 之前,毫无疑问 r 已经得到了 resolve,但是 Promise 的 resolve 始终是异步操作,所以 c 无法出现在 b 之前。

接下来我们试试跟 setTimeout 混用的 Promise。为了理解微任务始终先于宏任务,将Promise改成耗时1秒。

          setTimeout(()=>console.log("d"), 0)
          var r = new Promise(function(resolve, reject){
            resolve()
          });
          r.then(() => {
            var begin = Date.now();
            while(Date.now() - begin < 1000);
            console.log("c1")
            new Promise(function(resolve, reject){
              resolve()
          }).then(() => console.log("c2"))
          });

这里我们强制了 1 秒的执行耗时,这样,我们可以确保任务 c2 是在 d 之后被添加到任务队列。

我们可以看到,即使耗时一秒的 c1 执行完毕,再 enque 的 c2,仍然先于 d 执行了,这很好地解释了微任务优先的原理

通过一系列的实验,我们可以总结一下如何分析异步执行的顺序:

1、首先我们分析有多少个宏任务;
2、在每个宏任务中,分析有多少个微任务;
3、根据调用次序,确定宏任务中的微任务执行次序;
4、根据宏任务的触发规则和调用次序,确定宏任务的执行次序;(同一个宏任务下的微任务始终于这个宏任务前执行:可参考:https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/ )
5、确定整个顺序。

        function sleep(duration) {
          return new Promise(function(resolve, reject) {
          console.log("b");
          setTimeout(resolve,duration);
          })
        }
        console.log("a");
        sleep(5000).then(()=>console.log("c"));

这是一段非常常用的封装方法,利用 Promise 把 setTimeout 封装成可以用于异步的函数。

我们首先来看,setTimeout 把整个代码分割成了 2 个宏观任务,这里不论是 5 秒还是 0 秒,都是一样的。

第一个宏观任务中,包含了先后同步执行的 console.log(“a”); 和 console.log(“b”);。

setTimeout 后,第二个宏观任务执行调用了 resolve,然后 then 中的代码异步得到执行,所以调用了 console.log(“c”),最终输出的顺序才是: a b c。

这里应该更能了解Promise和setTimeout以及其他代码的执行顺序了。

本文参考于  winter 老师的重学前端课程,有兴趣的小伙伴也可以去了解一下。

Promise里的代码为什么比setTimeout先执行的更多相关文章

  1. 重学前端 --- Promise里的代码为什么比setTimeout先执行?

    首先通过一段代码进入讨论的主题 var r = new Promise(function(resolve, reject){ console.log("a"); resolve() ...

  2. try{}里有一个return语句,那么紧跟在这个try后的finally{}里的代码会不会被执行,什么时候被执行,在return前还是后?

    答:会执行,在方法返回调用者前执行.

  3. promise、async、await、settimeout异步原理与执行顺序

    一道经典的前端笔试题,你能一眼写出他们的执行结果吗? async function async1() { console.log("async1 start"); await as ...

  4. promise.then, setTimeout,await执行顺序问题

    promise.then VS setTimeout 在chrome和node环境环境中均输出2, 3, 1, 先输出2没什么好说的,3和1顺序让人有些意外 原因: 有一个事件循环,但是任务队列可以有 ...

  5. Promise里捕捉错误的最佳实践

    Promise里的同步部分不需要try catch new Promise((resolve, reject) => { throw new Error('error'); setTimeout ...

  6. 设置word里的代码格式,使之有底纹的效果

    目录 1    实现效果:    1 2    怎么才能在word里实现这样的显示?    1 如何设置word里的代码格式,使之有底纹的效果    2     实现效果: 怎么才能在word里实现这 ...

  7. word里的代码格式,使之有底纹的效果

      实现效果: 怎么才能在word里实现这样的显示? 如何设置word里的代码格式,使之有底纹的效果

  8. git如何merge github forked repository里的代码更新?(转)

    参考内容:git如何merge github forked repository里的代码更新? [refer to ]http://www.haojii.com/2011/08/how-to-git- ...

  9. Eclipse里的代码光标变成一个黑色块

    以前经常在编写程序是不知到碰到键盘上的那个键了,或是那几个组合键了,使得Eclipse里的代码光标变成一个黑色块:在这个状态下,光标不在活动自如,只能一直往后写代码,就不想平时的 " | & ...

随机推荐

  1. 2007年NOIP普及组复赛题解

    题目涉及算法: 奖学金:结构体排序: 纪念品分组:贪心: 守望者的逃离:动态规划: Hanoi 双塔问题:递推. 奖学金 题目链接:https://www.luogu.org/problem/P109 ...

  2. Math.abs( x )

    Math.abs( x ) 下面是参数的详细信息: x : 一个数字 返回值: 返回一个数字的绝对值 <html> <head> <title>JavaScript ...

  3. hdu 4394 Digital Square(bfs)

    Digital Square Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  4. tensorflow在文本处理中的使用——CBOW词嵌入模型

    代码来源于:tensorflow机器学习实战指南(曾益强 译,2017年9月)——第七章:自然语言处理 代码地址:https://github.com/nfmcclure/tensorflow-coo ...

  5. Vue2.0 Vue.set的使用

    原文链接: https://blog.csdn.net/qq_30455841/article/details/78666571

  6. Linux 内核释放函数和 kobject 类型

    讨论中仍然缺失的一个重要事情是当一个 kobject 的引用计数到 0 时会发生什么. 创建 kobject 的代码通常不知道什么时候要发生这个情况; 如果它知道, 在第一位使用一个引 用计数就没有意 ...

  7. Android7_安卓的知识体系梳理

    最近梳理了一下安卓的知识体系,先构建一个整体性的认知,也作为以后的学习路线的依据. [一.从原理角度出发]1.Activity生命周期和启动模式2.View的事件体系与工作原理3.四大组件的工作过程4 ...

  8. 第二阶段:2.商业需求分析及BRD:2.产品需求池

    需求获取方式 比如公司战略方面的需求  用户的反馈:投诉 建议等等 产品经理需要时刻关注竞品以及行业的发展! 需求池:各个产品经理的需求总和成一个需求池.让资源更好的利用起来.有的公司还有个“需求管理 ...

  9. 超简单本地mock假数据测试,模拟后台数据返回必杀技

    温馨提示:急性子可以直接拉到最后观看方法步骤. 什么是mock? mock就是在开发过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试开发的方法. 使用mock有什么好处? ...

  10. 一次面试题,将 字符串 保存在 Byte 数组中

    最近在面试,遇到一个面试题 字符串 String str = "AD428C93DE" 编程实现把 str 的内容放到 Byte[6] b 的数组中,存入后并能恢复原来的字符串. ...