【Code】promise与async/await

308 阅读7分钟

一、Promise

(一)Promise的作用

  • 主要是用来解决回调嵌套的问题(执行完后再去执行某一些操作,这时候容易形成嵌套再嵌套的问题),也就是常见的"回调地狱";
  • 执行多并发请求获取数据;

(二)Promise 的基本流程

image.png

(三)Promise 的基本特征

  1. promise 有三个状态:pending(默认),fulfilled(完成),or rejected(失败);「规范 Promise/A+ 2.1」
    • promise 的默认状态是 pending
    • promise 只能从pendingrejected, 或者从pendingfulfilled
    • 状态一旦确认,就不会再改变 ☆☆☆
  2. promise 有两个保存状态结果的值,分别是value和reson
    • value保存成功状态的值,可以是undefined/thenable/promise;「规范 Promise/A+ 1.3」
    • reason保存失败状态的值;「规范 Promise/A+ 1.5」
  3. new promise时, 需要传递一个executor()执行器,执行器立即执行☆☆☆;
    executor接受两个参数,分别是resolvereject
    • 调用resolve方法就是成功,
    • 调用reject方法就是失败
  4. promise 必须有一个then方法,then 接收两个参数,分别是 promise 成功的回调 onFulfilled, 和 promise 失败的回调 onRejected;「规范 Promise/A+ 2.2」
    • 如果调用 then 时,promise 已经成功,则执行onFulfilled,参数是promisevalue
    • 如果调用 then 时,promise 已经失败,那么执行onRejected, 参数是promisereason
  5. 如果 then 中抛出了异常,那么就会把这个异常作为参数,传递给下一个 then 的失败的回调onRejected

(四)手写实现Promise

1. 前置知识点

JavaScript constructor 属性详解
constructor属性:返回对创建此对象的数组函数的引用。
constructor属性: 并不是所有对象都有此属性。每个构造函数刚被制造出来的时候,它的c.prototype上面都会有一个constructor属性,并且执行它本身,即 c.prototype.constructor ===c;

2. 手写简易版的Promise

// 三个状态:PENDING、FULFILLED、REJECTED
//  javascript 对象中的 constructor属性 指向的函数本身。
const PENDING = 'PENDING'; //
const FULFILLED = 'FULFILLED'; //完成
const REJECTED = 'REJECTED';

class Promise {
    // 每个构造函数C刚被制造出来的时候,它的C.prototype上面都会有一个constructor属性,并且执行它本身
    // constructor 属性返回对创建此对象的数组函数的引用。
    // 【1】立即执行函数
    constructor(executor) {
        console.log(0, '创建Promise对象');
        // (1) promise 的 三个状态
        this.status = PENDING; // 默认状态为 PENDING
        // (2) promise 有两个保存状态结果的值
        this.value = undefined; // 存放成功状态的值,默认为 undefined
        this.reason = undefined; // 存放失败状态的值,默认为 undefined
        this.successCB = []; // 成功存放的数组
        this.failCB = []; // 失败存放的数组
        // 【1-1】调用此方法就是成功
        let resolve = value => {
            // 状态为 PENDING 时才可以更新状态,
            //防止 executor 中调用了两次 resovle/reject 方法
            if (this.status === PENDING) {
                console.log(2, '成功则调用 resolve 方法');
                this.status = FULFILLED;
                this.value = value;
                this.successCB.forEach(f => f()); //
            }
        };
        // 【1-2】调用此方法就是失败
        let reject = reason => {
            //确保状态还未改变
            if (this.status == PENDING) {
                console.log(3, '失败则调用 reject 方法');
                this.status = REJECTED;
                this.reason = reason;
                this.failCB.forEach(f => f());
            }
        };
        // 【1-3】立即执行函数
        try {
            console.log(1, '立刻执行函数executor(resolve, reject)');
            // 立即执行,将 resolve 和 reject 函数传给使用者
            executor(resolve, reject);
        } catch (error) {
            console.log(3, '出错则调用reject函数 error');
            reject(error); //出错则调用reject函数
        }
    }
    //【2】异步函数
    // 包含一个 then 方法,并接收两个参数 onFulfilled、onRejected,
    // 第一个参数是状态变为成功后应该执行的回调函数,
    // 第二个参数是状态变为失败后应该执行的回调函数。
    then(onFulfilled, onRejected) {
        console.log(4, 'then:当前状态', this.status);
        // 【2-1】成功后用户需要执行的函数
        if (this.status === FULFILLED) {
            //onFulfilled:成功后用户需要执行的函数
            console.log(5, '成功后执行 onFulfilled函数');
            onFulfilled(this.value);
        }
        // 【2-2】失败后用户需要执行的函数
        if (this.status === REJECTED) {
            //onRejected:失败后用户需要执行的函数
            console.log(6, '失败后执行 onRejected函数');
            onRejected(this.reason);
        }
        // 【2-3】
        if (this.status === PENDING) {
            this.successCB.push(() => {
                onFulfilled(this.value);
            });
            this.failCB.push(() => {
                onRejected(this.reason);
            });
        }
    }
}

调用

const ps = new Promise((resolve, reject) => {
  if (成功) {
    resolve('成功了');// 状态由等待变为成功,传的参数作为then函数中成功函数的实参
  } else {
    reject('失败了'); // 状态由等待变为失败,传的参数作为then函数中失败函数的实参
  }
})
ps.then(data => {
  console.log('success----', data);
}, (err) => {
  console.log('fail----', err);
});

3. 手写promise.all()

promise.all() 里面状态都改变,那就会输出,得到一个数组

/*
Promise函数对象的all方法
返回一个promise,只有当所有promise都成功时才成功,否则只要有一个失败的就失败
*/
Promise.all = function (promisesArr) {
    let resolvedCount = 0; // 用来保存成功promise的数量
    const resolvedArr = new Array(promisesArr.length); // 创建一个数组用来保存所有promise执行成功后返回的对应的value
    // 【1】 返回一个新的promise
    return new Promise((resolve, reject) => {
        // 【2】 遍历promises获取每个promise的结果
        promisesArr.forEach((promiseItem, index) => {
            promiseItem.then(
                value => {
                    resolvedCount++; // 成功的数量加1
                    resolvedArr[index] = value; // p成功,将成功的value按顺序保存到values
                    // 【3】 如果全部成功了。将return的新的promise的状态改为成功
                    if (resolvedCount === promisesArr.length) {
                        resolve(resolvedArr); //返回所有promise的执行结果
                    }
                },
                reason => {
                    // 【4】 只要有一个失败了,return的promise就失败
                    reject(reason);
                }
            );
        });
    });
};

4. 手写promise.race()

promise.race() 里面只有一个状态变为rejected或者fulfilled即输出

/*
Promise函数对象的race方法
返回一个promise,其结果由第一个完成的promise决定
*/
Promise.race = function (promisesArr) {
    // 【1】 返回一个promise
    return new Promise((resolve, reject) => {
        // 【2】 遍历promises获取每个promise的结果
        promisesArr.forEach((promiseItem, index) => {
            promiseItem.then(// 只有第一次调用的有效果
                value => {
                    resolve(value);// 一旦有成功,将return的promise变为成功
                },
                reason => {
                    // 【3】一旦有失败,将return的promise变为失败
                    reject(reason);
                }
            );
        });
    });
};

5. promise.finally()

不管指定不管Promise对象最后状态如何,都会执行的操作(本质上还是then方法的特例)

二、async/await

(一)async/await的实现原理

  • async/await是基于Promise构建的一种语法糖,它利用了Promise的特性来支持暂停和恢复函数的执行,并使用try/catch来处理异步操作中的错误。
  1. async函数:使用async关键字定义的函数会返回一个Promise对象。内部代码可以包含await关键字,用于暂停函数的执行,等待一个异步操作完成并返回结果。
  2. await关键字:await关键字只能在async函数内部使用。当遇到await时,它会暂停async函数的执行,等待后面的表达式(通常是一个返回Promise对象的异步操作)解析为已完成状态,并且返回该操作的结果。
  3. Promise对象:await关键字只能用于Promise对象,它会等待该Promise对象的状态变为已完成或已拒绝。如果状态为已完成,则await表达式返回已完成的值;如果状态为已拒绝,则会抛出异常。
  4. 错误处理:使用try/catch块来捕获await表达式中的异常,并进行相应的错误处理。如果await表达式中的Promise被拒绝,将会抛出一个错误,可通过catch块捕获并进行处理。

[ 问题延伸]

1. js实现限制函数并发量限制

2. async/await 和 Promise 有什么区别

  • async/await是一种用于处理异步操作的语法糖,它基于Promise构建,并提供更简洁、更直观的编码方式。它们的区别如下:
  1. 语法:使用async/await可以以同步代码的方式编写异步操作,而Promise则需要通过.then().catch()等方法链式调用来处理异步结果。
  2. 错误处理:在Promise中,错误处理通常需要使用.catch()来捕获和处理异常。而在async/await中,可以使用try/catch块来直接捕获和处理异常,使错误处理更加简洁。
  3. 可读性async/await可以使异步代码看起来更像是同步代码,流程更加清晰易懂。相比之下,使用Promise进行异步编程可能会导致回调地狱(callback hell)的问题,难以维护和阅读。
  4. 异步函数返回值:使用Promise时,.then()方法返回的是一个新的Promise对象,可以对其进行链式调用。而在async/await中,异步函数的返回值是一个Promise对象,可以直接使用await关键字来获取异步操作的结果。

参考链接: