重学前端学习笔记(十七)--Promise里的代码为何比setTimeout先执行?

笔记说明

重学前端是程劭非(winter)【前手机淘宝前端负责人】在极客时间开的一个专栏,天天10分钟,重构你的前端知识体系,笔者主要整理学习过程的一些要点笔记以及感悟,完整的能够加入winter的专栏学习【原文有winter的语音】,若有侵权请联系我,邮箱:kaimo313@foxmail.com。前端

1、引言

一个 JavaScript 引擎会常驻于内存中,它等待着 宿主JavaScript 代码或者函数传递给它执行。浏览器

一、ES3版本以及以前,JavaScript 自己尚未异步执行代码的能力,宿主环境传递给 JavaScript 引擎,而后按顺序执行,由宿主发起任务。异步

二、ES5以后,JavaScript 引入了 Promise,不须要浏览器的安排,JavaScript 引擎自己也能够发起任务。async

三、采纳JSC引擎术语,把宿主发起的任务称为宏观任务,把JavaScript引擎发起的任务称为微观任务函数

2、宏观和微观任务

JavaScript 引擎等待宿主环境分配宏观任务,在 Node 术语中,把这个部分称为事件循环学习

一、用伪代码来表示:跑在独立线程中的循环ui

while(TRUE) {
    r = wait();
    execute(r);
}
复制代码

二、整个循环作的事情基本上就是反复 等待 - 执行,这里的执行过程,其实都是一个宏观任务。能够大体理解为:宏观任务的队列就至关于时间循环。spa

三、在宏观任务中,JavaScriptPromise 还会产生异步代码JavaScript 必须保证这些异步代码在一个宏观任务中完成,所以,每一个宏观任务中又包含了一个微观任务队列:以下图所示线程

宏观任务中的微观任务队列

例如:Promise 永远在队列尾部添加微观任务。setTimeout 等宿主 API,则会添加宏观任务。code

3、Promise

JavaScript 语言提供的一种标准化的异步管理方式,当进行 io、等待或者其它异步操做的函数,不返回真实结果,而返回一个承诺,函数的调用方能够在合适的时机,选择等待这个承诺兑现

3.一、基本用法示例

function sleep(duration) {
        return new Promise(function(resolve, reject) {
            setTimeout(resolve,duration);
        })
    }
    sleep(1000).then( ()=> console.log("finished"));
复制代码

Promisethen 回调是一个异步的执行过程。

3.二、Promise 函数中的执行顺序

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

    // 输出顺序:a b c
复制代码

3.三、setTimeout 混用的 Promise

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

    // 输出顺序:a b c d
复制代码

Promise 产生的是 JavaScript 引擎内部的微任务,而 setTimeout 是浏览器 API,它产生宏任务。因此d 一定在 c 以后输出。

3.四、一个耗时 1 秒的 Promise

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

    // 输出顺序:c1 c2 d
复制代码

这个例子很好的解释了微任务优先的原理

3.五、如何分析异步执行的顺序

  • 一、首先咱们分析有多少个宏任务
  • 二、在每一个宏任务中,分析有多少个微任务
  • 三、根据调用次序,肯定宏任务中的微任务执行次序
  • 四、根据宏任务的触发规则和调用次序,肯定宏任务的执行次序
  • 五、肯定整个顺序
function sleep(duration) {
        return new Promise(function(resolve, reject) {
            console.log("b");
            setTimeout(resolve,duration);
        })
    }
    console.log("a");
    sleep(5000).then(()=>console.log("c"));

    // 输出顺序:a b c(c要等5秒)
复制代码

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

setTimeout 后,第二个宏观任务执行调用了 resolve,而后 then 中的代码异步获得执行,调用了 console.log("c")

4、新特性:async/await

async/awaitES2016 新加入的特性,它提供了用 for、if 等代码结构来编写异步的方式,而且运行时基础是 Promise

一、async 函数是在 function 关键字以前加上 async 关键字,这样就定义了一个 async 函数,能够在其中使用 await 来等待一个 Promise

function sleep(duration) {
    return new Promise(function(resolve, reject) {
        setTimeout(resolve,duration);
    })
}
async function foo(){
    console.log("a")
    await sleep(2000)
    console.log("b")
}

foo();

// 输出顺序:a b(b要等两秒)
复制代码

二、async 嵌套

function sleep(duration) {
    return new Promise(function(resolve, reject) {
        setTimeout(resolve,duration);
    })
}
async function foo(name){
    await sleep(2000)
    console.log(name)
}
async function foo2(){
    await foo("a");
    await foo("b");
}

foo2();

// 输出顺序:a(a等两秒) b(b也等两秒)
复制代码

我的总结

看完这篇确实帮我解决了以前困扰个人问题,赞一个。。。明天也要加油鸭。。。。

相关文章
相关标签/搜索