Javascript异步操作(Promise原理解析)真看这一篇就行了

目录:

1、同步和异步

2、事件循环

3、Promise

  • 3.1、Promise使用
  • 3.2、Promise原理解析

4、await和async


1、同步和异步:

首先,在学习js中同步异步的问题前,需要明白,js是单线程的,为什么它得是单线程的呢?这得从它的使用场景来看了,它主要是用来让用户与页面进行交互的吧。那么假设js是多线程的,在这个线程里面,用户点击某个按钮会增加一个DOM节点,在另一个线程里面,用户点击这个按钮又会删除一个DOM节点,那么此时js就不知道该听谁的了。 JavaScript 是单执行绪的程式语言,一行代码执行完才会再执行下一行,这个概念称之为同步 (synchronous)。但这样其实会遇到一个问题,试想一个情境:假设有一网站需要去服务器端拿取资料,但需要等十秒之后才会拿到,此外等待途中网站无法执行任何动作,这对于使用者来说,会认为画面定格十秒钟、就像当机一样,绝对是很糟糕的使用者体验,于是就有了异步(asynchronous) 的概念。

异步的代码或事件,并不会阻碍主线程执行其他代码,以上面网站向服务器拿取资料为例,拿取资料当作是一个异步事件,异步事件会在完成之后再通知主线程,而在这之中,主线程可以继续执行其他代码、使用者互动也不受异步事件的阻挡。而浏览器或其他的执行环境 (例如 Node.js) 之所以能够实践异步,正是因为有事件循环 (Event loop) 的机制。透过事件循环机制,能有效解决 JavaScript 单执行绪的问题,让耗时的操作不会阻塞主线成。

简单的说:

  • 同步任务:在主线程上排队执行的任务只有前一个任务执行完毕,才能执行后一个任务。
  • 异步任务:不进入主线程,而是进入任务队列。 当主线程中的任务执行完毕,就从任务队列中取出任务放进主线程中来进行执行。由于主线程不断重复的获得任务、执行任务、再获取再执行,所以者种机制被叫做事件循环(Event Loop)

2、事件循环:

事件循环不存在 JavaScript 本身,而是由 JavaScript 的执行环境 (浏览器或 Node.js) 来实现的,其中包含几个概念:

  • 栈 (Stack):采用后进先出的规则,当函式执行时,会被添加到栈的顶部,当执行完成时,就会从顶部移出,直到栈被清空;
  • 队列 (Queue):也是一种数据结构,特性是先进先出 (FIFO)。在 JavaScript 的执行环境中,等待处理的任务会被放在队列(Queue) 里面,等待栈 (Stack) 被清空时,会从队列(Queue)中拿取第一个任务进行处理;

事件循环 (Event loop):事件循环会不断地去查看栈(Stack) 是否空出,如果空出就会把队列 (Queue)中等待的任务放进栈(Stack)中执行;

事件循环又叫消息循环,是浏览器渲染主线程的工作方式。浏览器开启一个永不停止的for循环,每次循环都会从消息队列中取任务,其他线程只需要在合适的时候将任务加入到消息队列的末尾。

异步任务队列主要分为宏任务与微任务两种。ES6 规范中,宏任务(Macrotask) 称为 Task, 微任务(Microtask) 称为 Jobs。宏任务是由宿主(浏览器、Node)发起的,而微任务由 JS 自身发起的。事件循环的过程中,执行栈在同步代码执行完成后,优先检查微任务队列是否有任务需要执行,如果没有,再去宏任务队列检查是否有任务执行,如此往复。微任务一般在当前循环就会优先执行,而宏任务会等到下一次循环,因此,微任务一般比宏任务先执行,并且微任务队列只有一个,宏任务队列可能有多个。

任务队列分类: 宏任务队列:setTimeout的回调属于宏任务、setInterval的回调属于宏任务、DOM 事件; 微任务队列:"Promise"的回调属于微任务;

例如:

javascript 复制代码
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");

输出结果如下: 输出顺序会是1、4、3、2。因为"Promise"的"then"是微任务,会在当前执行栈("console.log("1"); console.log("4");")结束后立即执行,而"setTimeout"是宏任务,会在下一个事件循环执行。


3、Promise原理解析:

在javascript中异步任务执行有两种方式一种是回调函数一种是promise。

  • 回调函数: 和同步操作不同,异步操作即不会立即返回结果的操作(如发起网络请求,下载文件,操作数据库等)。如果我们后续的函数需要之前函数返回的结果,又怎样使之前的异步操作在其完成时通知到后续函数来执行呢? 通常,我们可以将这个函数先定义,存储在内存中,将其当做参数传入之前的异步操作函数中,等异步操作结束,就会调用执行这个函数,这个函数就叫做回调函数(callback)。 那假如callback函数同样是个异步函数,且callback里又嵌入了callback呢? 如此一来,嵌套太深容易引发"回调地狱",即代码只会横向发展,不好管理。为此,对于那些需要连续执行的异步操作,Promise可以是一种很好的解决办法。

  • Promise:为了解决回调函数的问题出现了Promise(该含义为请求会在未来的一个时刻返回数据给调用者),也表示一个异步操作的最终完成或失败的结果。

3.1、Promise使用:

Promise是一个构造函数,我们需要通过new来创建,而Promise会接收一个函数作为参数,这个函数有称为执行函数(exector)是我们自定义的函数,然后这个(exector)函数需要两个入参resolve和reject,这俩参数也是javascript的默认函数。resolve函数在(executor)我们自定义函数执行成功后会调用resolve返回结果并修改Promise实例状态,reject函数在(executor)我们自定义函数执行失败会调用reject返回结果并修改Promise实例状态。一旦创建Promise对象且传入我们自定义的回调函数,这个自定义的函数就会立刻执行(可以理解为启动了一个线程在后台执行)。语法如下:

typescript 复制代码
//Promise函数的入参数void表示函数是没返回或者无需入参:
executor: (
    resolve: (value: unknown) => void,
    reject: (reason?: any) => void
) => void

//创建一个promise对象
let promise = new Promise(function(resolve, reject) {
  // executor 
})

Promise实例对象会有两个属性用来保存状态和结果,状态属性对应的值如下:

  • pending:初始状态,表示执行了我们自定义的函数(executor),但还在等待中。
  • fulfilled:异步函数执行成功执行 resolve 函数,表示操作完成。
  • rejected:异步函数执行失败执行reject 函数,表示操作失败。
1)、then使用:

Promise 实例状创建完成后就会执行我们定义的回调函数处理业务逻辑,那么我们要是想嵌套多个回调函数怎么处理(比如外层函数处理获取数据,然后嵌套对外层函数获取到的数据进行处理,再嵌套对处理后的数据进行保存),这里我们可以用.then()方法来实现这些的回调函数的嵌套处理。

scss 复制代码
getData(function(data) {
  processData(data, function(result) {
    saveResult(result, function() {
      // 更多嵌套...
    });
  });
});
//Promise 的优势:链式调用(.then)让代码扁平化。
getData()
  .then(processData)
  .then(saveResult)
  .catch(handleError);

.then()方法触发调用是在Promise实例态改变之后(即变为fulfilled和reject状态),就会触发后面对应的 .then() 方法里的函数(成功函数和失败函数),继续执行后续步骤(简单的说就是.then方法会捕捉到当前Promise实例对象执行的成功和失败来选择执行.then方法里面对应的两个函数,从而最终实现上面提到的回调函数嵌套的功能)。另外,Promise 的实例状态只会改变一次,确定为 fulfilled 或 rejected 之一后就不会再变。".then()"方法的两个入参函数分别为第一个为"成功函数",第二个为"失败函数",如下:

typescript 复制代码
then(
  onfulfilled?: ((value: unknown) => (PromiseLike<void> | void)) | undefined | null,    
  onrejected?: ((reason: any) => PromiseLike<never>) | undefined | null, 
)

onfulfilled:该匿名函数接收的值为Promise实例执行resolve后返回的值; onrejected:该匿名函数接收的值为Promise实例执行reject后返回的值; 如果只想处理成功,或者失败,可以把".then()"方法里面的对应参数设置为null,只有一个参数则表示状态改变就执行,不区分状态结果。

示例如下:

javascript 复制代码
let p = new Promise((resolve, reject) => {  //A
    resolve("Executor自定义函数执行成功"); // 调用resolve()方法该方法的入参会变为结果传递给then()方法的"成功函数"
});

p.then(
    (res) => { //B
        console.log("回调函数A,调用链路为:Executor->A");
        console.log("Executor自定义函数执行成功后回调该函数,Executor自定义函数执行结果为:", res);
        /*
        当前.then()方法中"成功函数"的回调函数的输入值是上一个Promise实例对象执行成功后的返回值,
        然而这里的回调函数执行完后的返回值为空,所以后面的".then()"方法的"成功函数"的回调函数的输入值就为空.

        若我们想要后面的".then()"方法的"成功函数"的回调函数的输入值不为空,我可可以用return来返回我们需要的内容。
        例如:
        return "函数A执行成功";
        或者返回一个Promise实例对象,然后执行该对象的resolve()方法来返回我们需要的内容。
        例如:
        return new Promise((resolve, reject) => {
            resolve("函数A执行成功");
        });
        */

        //return "函数A执行成功";

        return new Promise((resolve, reject) => {
            resolve("函数A执行成功");
        });

    },
    (err) => {
        console.log("回调函数A,调用链路为:Executor->A");
        console.log("Executor自定义函数执行失败后回调该函数,Executor自定义函数执行结果为:", err);
    }
).then(
    (res) => { //C
        console.log("回调函数B,调用链路为:Executor->A->B");
        console.log("回调函数A执行成功后回调该函数,上个函数的执行结果为:", res);
    },
    (err) => {
        console.log("回调函数B,调用链路为:Executor->A->B");
        console.log("回调函数A执行失败后回调该函数,上个函数的执行结果为:", err);
    }
)
  • //A:上面的我们创建了一个Promise实例对象p,然后在自定义函数里面直接执行resolve函数表示我们的自定义函数执行成功;
  • //B:然后使用p.then方法来捕获我们的自定义函数执行成功(即Promise实例对象的状态变为"fulfilled"),然后触发执行p.then里面的"成功函数"达到回调函数嵌套的目的,然后在该函数里返回一个新的Promise实例对象同时执行该对象中自定义函数的resolve方法触发下一个.then方法去回调对应的函数;
  • //C:捕获到B处的自定义回调函数执行成功,然后调用当前.then方法中对应的"成功函数";

上面的输出结果为: 回调函数A,调用链路为:Executor->A Executor自定义函数执行成功后回调该函数,Executor自定义函数执行结果为: Executor自定义函数执行成功 回调函数B,调用链路为:Executor->A->B 回调函数A执行成功后回调该函数,上个函数的执行结果为: 函数A执行成功

2)、catch方法:

catch方法是then方法的补充,用于处理异步操作失败的结果。当Promise的状态变为rejected时,catch方法中的回调函数将被调用。回调函数的参数是异步操作失败的原因。另一个作用是,当执行 resolve 的回调(也就是上面 then 中的第一个参数)时,如果抛出异常了(代码出错了),那么也不会报错卡死 js,而是会进到这个 catch 方法中。catch()方法可以看作是.then()方法的简化版本只捕获Promise执行错误的状态(reject),该方法就只有一个入参,就是"匿名的失败函数"。示例如下:

javascript 复制代码
let p = new Promise((resolve, reject) => { //A
    resolve("Executor自定义函数执行成功"); // 调用resolve()方法该方法的入参会变为结果传递给then()方法的"成功函数"
});

p.then( //B
    (res) => {
        console.log("回调函数A,调用链路为:Executor->A");
        console.log("Executor自定义函数执行成功后回调该函数,Executor自定义函数执行结果为:", res);

        throw new Error("回调函数A执行过程中发生错误啦!");
    },
    (err) => {
        console.log("回调函数A,调用链路为:Executor->A");
        console.log("Executor自定义函数执行失败后回调该函数,Executor自定义函数执行结果为:", err);
    }
).then(
    (res) => {
        console.log("回调函数B,调用链路为:Executor->A->B");
        console.log("回调函数A执行成功后回调该函数,上个函数的执行结果为:", res);
    },
    (err) => { //C
        console.log("回调函数B,调用链路为:Executor->A->B");
        console.log("回调函数A执行失败后回调该函数,上个函数的执行结果为:", err);
        return err;
    }
).catch((err) => { //D
    console.log("捕获到执行失败的函数,错误信息:",err);
})
  • //A:上面的我们创建了一个Promise实例对象p,然后在自定义函数里面直接执行resolve函数表示我们的自定义函数执行成功;
  • //B:然后使用p.then方法来捕获我们的自定义函数执行成功(即Promise实例对象的状态变为"fulfilled"),然后触发执行p.then里面的"成功函数"达到回调函数嵌套的目的,这个回调函数中我们手动模拟一执行过程中的抛出错误;
  • //C:捕获到B处的自定义回调函数执行失败,要是 //B处有"执行失败函数"那么下面的.catch方法将不会捕获到错误信息;
  • //D:要是//C处的then方法没有定义错误处理的函数,那么就会被.catch()方法捕获到错误信息;

输出结果为:

less 复制代码
回调函数A,调用链路为:Executor->A
Executor自定义函数执行成功后回调该函数,Executor自定义函数执行结果为: Executor自定义函数执行成功
回调函数B,调用链路为:Executor->A->B
回调函数A执行失败后回调该函数,上个函数的执行结果为: Error: 回调函数A执行过程中发生错误啦!
    at /test.js:10:15
3)、finally使用

finally方法就是promise的状态不管是fulfilled还是rejected都会执行finally方法。如下情景,一进入页面先从服务器上获取数据,等待的同时会展示loading页面,而最后不论是否从服务器上获取数据都要关闭连接。

javascript 复制代码
fetch("https://explainthis.com/data")
  .then((response) => response.json())
  .then((data) => {
    console.log(data);
  })
  .catch((error) => {
    console.error(error);
  })
  .finally(() => {
    console.log("close loader");
  });

3.2、Promise原理解析:

下面我们用class类来实现一个Promise,先分析下,上面我们知道Promise是一个构造函数存在如下几个特性:

  • 其对象有三种状态分别为,pending(进行中)、fulfilled(已成功)和rejected(已失败)以及拥有一个value属性用于存放回调函数执行结果内容,所以需要有两个属性分别为"status"和一个"value",然后一旦实例状态变更后就无法在改变。故我们需要在类中定义"status"和一个"value"两个属性。
  • 同时在创建Promise构造函数的时候传入一个我们自定义的函数,然后我们自定义的函数就会立刻执行。所以在类的构造函数中有一个入参,然后在类的构造函数中直接执行该入参(我们自定义的函数)。
  • 我们自定义的回调函数,有两个js默认提供的resolve和reject这两个方法,用于候修改Promise实例对象的属性。所以类中还需要定义resolve和reject方法接收一个入参用于修改实例对象的"value"属性,以及执行这两个方法时候修改"status"属性为对应的内容。
  • 在创建好Promise对象的时候我们自定义函数执行的时候要是报错需要将错误引导到reject方法里面。所以在我们自己写的类的构造函数里面执行的时候还需要用try进行捕获错误;

then方法的特性:

  • 该方法接收两个匿名的回调函数作为入参。第一个回调函数是在当前Promise实例状态变为"fulfilled"的时候触发调用,同时接收Promise实例对象的"resolve"方法的执行返回结果内容;第二个回调函数是在当前Promise实例状态变为"rejected"的时候触发调用。同时接收Promise实例对象的"reject"方法的执行返回结果内容;
  • then支持链式调用,且都返回一个新的Promise实例对象;

实现如下:

kotlin 复制代码
// 自定义一个MyPromise类来实现Promise的功能
class MyPromise {
    /*
      1、在构造函数里面定义两个属性分别用于表示状态和值初始状态为pending,值为null。
      2、然后给构造函数接收一个入参,这个入参数就是一个用户自定义传入的回调函数,
         通过构造函数接收参数就可以实现在创建实例对象的时候就执行该回调函数。
      3、在构造函数里面调用这个用户自定义的回调函数,并且将resolve和reject方法作为参数传入。
     */
    constructor(executor) {
        this.status = "pending";
        this.value = null;
        this.onFulfilledCallbacks = []; // 用来保存then方法链式成功的回调(处理异步)
        this.onRejectedCallbacks = []; // 用来保存then方法链式失败的回调(处理异步)

        // 使用try来捕获我们自定义函数执行错误的时候将错误抛出给reject方法。
        try{
            // 这里的this指向的是该类的实例对象,但是executor是用户自定义的函数,
            // 当我们在自定义的函数里面执行传入的resolve和reject方法,那么resolve和reject方法中的this就指向了我们自定义函数对象(可能为global或window对象),
            // 就会导致代码执行的时候报错会提示读取不到status属性,所以我们需要使用bind()方法来绑定this的指向指回该MyPromise类的实例对象。
            executor(this.resolve.bind(this), this.reject.bind(this));
        }catch(err){
            this.reject(err);
        }

    }

    /*
      定义两个方法分别用于改变状态和值
      当状态为pending时才可以改变状态和值
     */

    // resolve方法用于改变状态为fulfilled,值为传入的值
    resolve(value) {
        if (this.status !== "pending") return;
        this.status = "fulfilled";
        this.value = value;

        // 当我们自定义的函数执行成功后调用then里面的回调
        while (this.onFulfilledCallbacks.length ){
            // 从数组中取出第一个回调函数并执行,执行完后删除该回调函数
            this.onFulfilledCallbacks.shift()(this.value);
        }
    }

    // reject方法用于改变状态为rejected,值为传入的值
    reject(value) {
        if (this.status !== "pending") return;
        this.status = "rejected";
        this.value = value;

        // 当我们自定义的函数执行失败后调用then里面的回调
        while (this.onRejectedCallbacks.length ){
            // 从数组中取出第一个回调函数并执行,执行完后删除该回调函数
            this.onRejectedCallbacks.shift()(this.value);
        }
    }

    then(onFulfilled, onRejected) {
        // 判断.then的第一个参数是不是一个函数,如果不是就直接作为结果返回,是的话就返回该函数
        onFulfilled = typeof onFulfilled === "function" ? onFulfilled : val => val;

        // 判断.then的第二个参数是不是一个函数,如果不是就直接作为错误返回,是的话就返回该函数
        onRejected = typeof onRejected == "function" ? onRejected : val => {throw val};

        // 定义一个新的MyPromise实例对象,因为then返回的是一个新的MyPromise的对象
        let thenPromise = new MyPromise((resolve, reject) => {

            // 定义一个箭头函数用callback参数接收入参,从而在调用then方法的时候可以执行onFulfilled还是onRejected两个回调函数
            const resolvePromise = callback => {
                setTimeout(()=>{
                    try {
                        //将当前MyPromise实例对象的值传入回调函数中执行。
                        //这里调用的callback实际上是onFulfilled或onRejected中的一个回调函数,
                        //且这两个回调函数的返回值有可能是一个普通值或者为一个新的MyPromise实例对象。
                        const x = callback(this.value);
                        if (x === thenPromise){
                            throw new Error('不允许返回自身!');
                        }

                        // 当回调函数返回一个新的MyPromise实例时,当前链式调用中的新 MyPromise(即 thenPromise)
                        // 需要根据这个返回的 MyPromise 的结果来决定自己的状态和值。
                        // 简单的说如果 x 是 MyPromise实例对象,则等待它的所有调用链条(then)的结果
                        if (x instanceof MyPromise){
                            x.then(resolve, reject);
                        }else {
                            resolve(x);
                        }
                    }catch (error) {
                        // 使用try的用意就是当then捕获当前MyPromise执行的结果是fulfilled状态的时候执行回调函数(onFulfilled)的时候
                        // 没有报错那么就将新创建的MyPromise实例对象的状态改为fulfilled(调用resolve方法),若then捕获到当前MyPromise执行的
                        // 结果是rejected状态的时候执行回调函数(onRejected)是直接返回错误的那么直接执行reject方法修改新创建的MyPromise实例对象
                        // 的状态为rejected。
                        reject(error);
                        throw new Error(error);
                    }
                })
            }

            // 新实例`thenPromise`在其构造函数中接收一个执行器函数(回调函数),该执行器函数中的`this`指向取决于它的调用方式。
            // 在代码中,执行器函数是以箭头函数的形式传入的,而箭头函数没有自己的`this`,它继承自外围作用域(即`then`方法的作用域)。
            // 因此,执行器函数中的`this`应该指向外层`then`方法中的`this`,即原Promise实例。
            // 所以这里的"this"实际上就是指向上一个MyPromise的实例对象。例如:
            // mp=MyPromise(...); mp.then(...)这里then内部新创建的MyPromise的代码中"this"就是指mp这个实例对象。
            // 简单的说就是捕获当前MyPromise的状态为fulfilled时调用then方法的onFulfilled入参
            if (this.status === "fulfilled"){
                resolvePromise(onFulfilled);
            }

            // 捕获当前MyPromise的状态为rejected时调用then方法的onRejected入参
            if (this.status ==="rejected"){
                resolvePromise(onRejected);
            }

            // 捕获上一个MyPromise的状态为pending时,将onFulfilled和onRejected入参保存起来,等待状态改变后再调用
            if (this.status === "pending"){
                this.onFulfilledCallbacks.push(resolvePromise.bind(this,onFulfilled));
                this.onRejectedCallbacks.push(resolvePromise.bind(this,onRejected));
            }

        })

        // then方法返回一个新的MyPromise实例对象,所以我们可以链式调用then方法
        return thenPromise;
    }
}


/*
 测试我们自定义实现的Promise
 */
let promise = new MyPromise((resolve, reject) => {
    resolve("Executor自定义函数执行成功");
    console.log("ok")
});

promise.then(
    (res) => {
        console.log("回调函数A,调用链路为:Executor->A");
        console.log("Executor自定义函数执行成功后回调该函数,Executor自定义函数执行结果为:", res);

        return "函数A执行成功";
    },
    (err) => {
        console.log("回调函数A,调用链路为:Executor->A");
        console.log("Executor自定义函数执行失败后回调该函数,Executor自定义函数执行结果为:", err);
    }
).then(
    (res) => {
        console.log("回调函数B,调用链路为:Executor->A->B");
        console.log("回调函数A执行成功后回调该函数,上个函数的执行结果为:", res);
        return new MyPromise((resolve, reject) => {
            resolve("函数B执行成功");
        })
    },
    (err) => {
        console.log("回调函数B,调用链路为:Executor->A->B");
        console.log("回调函数A执行失败后回调该函数,上个函数的执行结果为:", err);
        return err;
    }
)

执行流程示例: 创建一个 MyPromise 实例对象promise,其状态变为 fulfilled,值为"Executor自定义函数执行成功":

1)、调用 promise.then(onFulfilled):
  • then 方法创建新 MyPromise的实例对象thenPromise。
  • 由于 promise 的状态是 fulfilled,立即调用 resolvePromise(onFulfilled)。
  • resolvePromise回调函数执行onFulfilled函数,然后判断该函数执行的结果x。
2)、执行 resolvePromise:
  • 通过 setTimeout 异步执行回调。
  • 调用 onFulfilled("Executor自定义函数执行成功"),得到返回值 x。
  • 根据 x 的类型(普通值或 MyPromise)决定如何进一步处理thenPromise对象。
  • 若x返回的是一个新的MyPromise对象实例,那么调用该实例的then方法执行该后续的回调函数;
3)、解决 thenPromise:
  • 如果x的类型为普通值那么直接调用resolve方法将新创建的thenPromise对象的状态改为"fulfilled"。调用 resolve(x),thenPromise 变为 fulfilled。如下:
javascript 复制代码
p1.then((value) => value * 2)
  .then((result) => console.log(result)); // 输出 84
  
  promise.then(
    (res) => {
        console.log("回调函数A,调用链路为:Executor->A");
        console.log("Executor自定义函数执行成功后回调该函数,Executor自定义函数执行结果为:", res);
        return "函数A执行成功";
    }
).then(
    (res) => {
        console.log("回调函数B,调用链路为:Executor->A->B");
        console.log("回调函数A执行成功后回调该函数,上个函数的执行结果为:", res);
    }
) 

//输出如下:
回调函数A,调用链路为:Executor->A
Executor自定义函数执行成功后回调该函数,Executor自定义函数执行结果为: Executor自定义函数执行成功
回调函数B,调用链路为:Executor->A->B
回调函数A执行成功后回调该函数,上个函数的执行结果为: 函数A执行成功
  • 若 x 是 MyPromise,等待其解决后再调用 resolve 或 reject。
javascript 复制代码
promise.then(
    (res) => {
        console.log("回调函数B,调用链路为:Executor->A->B");
        console.log("回调函数A执行成功后回调该函数,上个函数的执行结果为:", res);
        return new MyPromise((resolve, reject) => {
            resolve("函数B执行成功");
        })
    }
).then(
    (res) => {
        console.log("回调函数C,调用链路为:Executor->A->B->C");
        console.log("回调函数B执行成功后回调该函数,上个函数的执行结果为:", res);
    }
)

第一个 then 的回调返回一个 新的MyPromise实例对象,它的输出是"函数B执行成功"。 第二个 then 会等待第一个回调返回的MyPromise实例对象的所有then(调用链)的执行结果解决后,在输出结果。


4、await和async:

await和async是基于promise之上的语法糖,比promise来使用异步操作更为简单。"async"关键词定义在函数声明之前,使函数变成异步函数,其返回一个Promise对象(那么我可以通过这个对象使用then来嵌入我们自定义的函数);"await"关键字用于等待一个Promise对象执行的结果,它只能在async函数中才起作用。通过使用它们,让异步代码看起来更加的简约易读。

javascript 复制代码
async function asyncTest1(){
    let rtl =  await asyncTest2();
    console.log("异步函数asyncTest1()执行结束,等待异步函数asyncTest2()执行的结果为:", rtl);
    return "异步函数asyncTest1()处理成功";
}

async function asyncTest2(){
    console.log("异步函数asyncTest2()执行结束");
    return "异步函数asyncTest2()处理成功";
}

// 调用异步函数asyncTest1(),该异步函数执行的时候通过await来回调异步函数asyncTest2()的执行并且等待其执行的结果,
// 然后将其结果赋值给变量rtl,最后将变量rtl返回给调用者,最后在调用asyncTest1()的then方法执行。
// async返回的是一个Promise对象,如果async函数没有返回值,async函数返回一个undefined的Promise对象
asyncTest1().then((res) => {
    console.log("asyncTest1()异步函数执行成功,结果为:", res);
})

输出结果为: 异步函数asyncTest2()执行结束 异步函数asyncTest1()执行结束,等待异步函数asyncTest2()执行的结果为: 异步函数asyncTest2()处理成功 asyncTest1()异步函数执行成功,结果为: 异步函数asyncTest1()处理成功


参考: 手撕Promise:Promise.then详解 juejin.cn/post/717300...

相关推荐
前端菜鸟来报道2 分钟前
html和css 实现元素顺时针旋转效果(椭圆形旋转轨迹)
前端·css·旋转·椭圆布局
Ustinian_3103 分钟前
【HTML】KaTeX 常用公式字符
前端·chrome·html
OpenTiny社区16 分钟前
直播分享|TinyPro:一行命令,搭建包含前后端的后台管理系统
前端·vue.js·github
怪兽也会哭哭20 分钟前
vue-如何将组件内容作为图片生成-html2canvas
前端·javascript·vue.js
aklry39 分钟前
原型与原型链
javascript
万少1 小时前
常见鸿蒙应用开发面试题
前端
云只上1 小时前
为什么后端接口返回数字类型1.00前端会取到1?
java·前端
枫无痕1 小时前
路由权限的分类与踩坑记录
前端
best6661 小时前
ServiceWoker是什么?
前端