Promise 的概念、用法与Typescript实现

举报
jcLee95 发表于 2023/06/08 21:29:22 2023/06/08
【摘要】 Promise 的概念、用法与 Typescript 实现 作者: 李俊才(CSDN:jcLee95) 邮箱 :291148484@163.com CSDN 主页:https://blog.csdn.net/qq_28550263?spm=1001.2101.3001.5343 本文地址:https://blog.csdn.net/qq_28550263/article/details/12...
Promise 的概念、用法与 Typescript 实现

作者李俊才(CSDN:jcLee95)
邮箱 :291148484@163.com
CSDN 主页https://blog.csdn.net/qq_28550263?spm=1001.2101.3001.5343
本文地址https://blog.csdn.net/qq_28550263/article/details/121506948

相关文章


目 录

1. Promise 的概念

2. ECMA Script Promise API

3. 使用 Typescript 实现 Promise

4. 附录


1. Promise 的概念

Promise 简介

Promise 是一个对象,它代表了一个异步操作的最终完成或者失败在其它一些语言如dart中也有这个概念,但叫做Future,表示将来。执行一个异步操作,在其执行前并不知道其最终执行会成功(即顺利完成:FULFILLED)或者会失败(即所谓拒绝:REJECTED),这时我们称这个一部操作执行的状态为待定(PENDING)

  • Promise 含义为“承若”,用于表示 一个异步操作的 最终完成/失败 及其 结果值
  • 一个 Promise 对象代表一个在这个 promise 被创建出来时不一定已知的值

它让您能够把异步操作最终的成功返回值或者失败原因相应的处理程序关联起来

这样使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个 promise,以便在未来某个时候把值交给使用者。

Promise 的状态

![在这里插入图片描述](https://img-blog.csdnimg.cn/77ad882e801b41c7bd95f2e08be43105.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAamNMZWU5NQ==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center =95%x)

2. ECMA Script Promise API

2.1 实例方法

2.1.1 then 方法

1. 语法格式

p.then(onFulfilled[, onRejected]);

2. 参数描述

该方法接受两个可选的回调,分别表示构造该 Promise 对象实例时,执行构造器函数后,实例状态由 pending 进入fulfilled 或者 rejected 后,所执行的回调。

也就是说:

  • 若该实例的状态由 pending 进入fulfilled 则执行 onFulfilled 回调;
  • 若该实例的状态由 pending 进入rejected 则执行 onRejected 回调;
参数 类型 描述
onFulfilled ((value: any) => void \| Thenable<void>) \| null \| undefined 当 Promise 变成接受状态(fulfilled)时调用的函数。
onRejected ((reason: any) => void \| Thenable<void>) \| null \| undefined 当 Promise 变成拒绝状态(rejected)时调用的函数。

其中,这里的 Thenable 也被称作 PromiseLike,表示实现了这样一个 then 方法的对象,见 5.1 Thenable 接口 小节。

3. 返回值

当一个 Promise 完成(fulfilled)或者失败(rejected)时,返回函数将被异步调用(由当前的线程循环来调度完成)。具体的返回值依据以下规则返回。如果 then 中的回调函数:

回调函数的返回值 then 返回的新 Promise
已接受的 Promise 对象实例 也会成为接受状态(fulfilled),
并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的 Promise 的接受状态回调函数的参数值。
已拒绝的 Promise 对象实例 也会成为拒绝状态(rejected),
并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的 Promise 的拒绝状态回调函数的参数值。
不是 Promise实例的其它值 将会成为接受状态(fulfilled),
并且将返回的值作为接受状态的回调函数的参数值。
没有返回任何值 将会成为接受状态(fulfilled),
并且该接受状态的回调函数的参数值为 undefined。
抛出异常 将会成为拒绝状态(rejected),
并且将抛出的错误作为拒绝状态的回调函数的参数值。
针对异步情况 返回一个未定状态(pending)的 Promise,那么 then 返回 Promise 的状态也是未定的,
并且它的终态与那个 Promise 的终态相同;
同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。

最简单的示例:

function excutor(resolve, reject) {
  // do something others ...
  // ...
    
  resolve('成功返回值');  // 调用传入执行器函数的 resolve 函数
}

let p1 = new Promise(excutor(resolve, reject));

p1.then(
  // 成功时执行的回调
  (value) => {
    console.log(value); // 成功返回值
  }, 
  // 失败时执行的回调
  (reason) => {
    console.error(reason); // 出错了!
  }
);

这个例子中,在执行器中执行了传入的 resolve 函数,使得 Promise 由 pending 状态进入了 fulfilled 状态。

4. 执行器函数 和 回调函数

在构造 Promise 对象时需要传入一个函数,称之为 执行器函数。这个函数有两个参数,分别是当前 Promsie 对象进入 成功/实现(fulfilled)、失败/拒绝(rejected) 这两个状态时的 回调函数。这两个函数的主要作用是,让我们(Promise对象的使用者)可以在执行器函数体内改变其状态,也就是实现 Promise 状态 分支 的作用。

也就是说,一个 Promise 对象构造之初为 pending状态 。通过调用不同的回调函数(resolve 或 reject),可以进而进入 fulfilled 状态 或 reject 状态。在前面的例子中,为了能让大家看得更清楚,我们是单独在外定义执行器函数的:

function excutor(resolve, reject) {
  // ...
  resolve('成功返回值');  // 调用传入执行器函数的 resolve 函数
}

然后我们将这个函数的执行后的结果作为 Promise 构造器函数的参数:

new Promise(excutor(resolve, reject));

但是显然你不能单独在Promise的构造器外调用执行器函数,获取该函数的返回值再传递给Promise的构造器:

let e = excutor(resolve, reject); // resolve & reject is not defined!
let p = new Promise(e);

这将产生 resolve 和 reject 没有定义的报错。这是因为,resolve 和 reject 这两个函数不是我们定义的,而是在 Promise 的构造器中进行定义的,使用 new Promise(excutor(resolve, reject)); 语句时,由 Promise 的构造器调传入了这两个参数。

作为 Promise 的使用者, 我们使用 resolve 和 reject 这两个在 Promise 内定义的回调函数,除了用于决定当前 Promise 将按照我们的规则进入那种状态(fulfilled 或 rejected)外,还有就是传入这两个状态该下需要传递的值:

  • 如果使用 resolve(value) 回调函数,表明当前 Promise 将进入 成功/兑现/实现 (fulfilled) 状态,这时根据需要一个传入resolve函数返回值,作为进一步处理的参数。
  • 如果使用 reject(reason) 回调函数,表明当前 Promise 将进入 失败/拒绝 (rejected) 状态,这时可以通过传入reject函数一个描述失败原因的值,以方便追踪为什么进入失败。

5. Promise 的链式调用

Promise 能够进行作为链式调用的原因在于,其 then 方法返回的也是一个 Promise 对象。

p.catch(onRejected);

p.catch(function(reason) {
   // 拒绝回调函数体
});

2.1.2 catch 方法

catch() 方法返回一个新的 Promise 对象的实例,并且处理拒绝的情况。它的行为与调用Promise.prototype.then(undefined, onRejected) 相同。
事实上,calling obj.catch(onRejected) 内部调用了 obj.then(undefined, onRejected) 方法。

例如:

var p = new Promise(function(resolve, reject) {
  throw 'Uh-oh!';
});

p.catch(function(e) {
  console.log(e); // "Uh-oh!"
});

在异步函数中抛出的错误不会被 catch 捕获到,例如:

var p2 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    throw 'Uncaught Exception!';
  }, 1000);
});

p2.catch(function(e) {
  console.log(e); // 不会执行
});

在 resolve() 后面抛出的错误会被忽略,例如

var p3 = new Promise(function(resolve, reject) {
  resolve();
  throw 'Silenced Exception!';
});

p3.catch(function(e) {
   console.log(e); // 不会执行
});

2.1.3 finally 方法

finally() 方法返回一个 Promise。在 promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行指定的回调函数。这为在 Promise 是否成功完成后都需要执行的代码提供了一种方式。这避免了同样的语句需要在 then() 和 catch() 中各写一次的情况。

p.finally(onFinally);

p.finally(function() {
   // 返回状态为 (resolved 或 rejected)
});

2.2 静态方法

2.2.1 all 方法

语法格式

Promise.any(iterable);

功能描述

该方法接收一个由 Promise 所组成的可迭代对象,并返回一个新的 promise:

  • 一旦可迭代对象内的任意一个 promise 变成了兑现状态,那么所返回的新 promise 就会变成兑现状态,并且它的兑现值就是可迭代对象内的首先兑现的 promise 的兑现值。
  • 如果可迭代对象内的所有 promise 最终都被拒绝,那么该方法所返回的 promise 就会变成拒绝状态,并且它的拒因会是一个 AggregateError 实例,这是 Error 的子类,用于把单一的错误集合在一起。

2.2.2 allSettled 方法

Promise.allSettled() 方法返回一个在所有给定的 promise 都已经fulfilled或rejected后的 promise,并带有一个对象数组,每个对象表示对应的 promise 结果。

当您有多个彼此不依赖的异步任务成功完成时,或者您总是想知道每个promise的结果时,通常使用它。

相比之下,Promise.all() 更适合彼此相互依赖或者在其中任何一个reject时立即结束。

语法格式

Promise.allSettled(iterable);

参数

参数 描述
iterable 一个可迭代的对象,例如Array,其中每个成员都是Promise。

2.2.3 any 方法

Promise.any() 接收一个由 Promise 所组成的可迭代对象,该方法会返回一个新的 promise,一旦可迭代对象内的任意一个 promise 变成了兑现状态,那么由该方法所返回的 promise 就会变成兑现状态,并且它的兑现值就是可迭代对象内的首先兑现的 promise 的兑现值。如果可迭代对象内的 promise 最终都没有兑现(即所有 promise 都被拒绝了),那么该方法所返回的 promise 就会变成拒绝状态。

语法格式

Promise.any(iterable);

参数

参数 描述
iterable 一个可迭代的对象,例如Array,其中每个成员都是Promise。

2.2.4 race 方法

该方法返回一个 Promise 实例,一旦迭代器中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝。

语法格式

Promise.race(iterable);

参数

参数 描述
iterable 一个可迭代的对象,例如Array,其中每个成员都是Promise。

2.2.5 reject 方法

该方法方法返回一个带有拒绝原因的 Promise 实例。

语法格式

Promise.reject(reason);

参数

参数 描述
reason 表示Promise被拒绝的原因

2.2.6 resolve 方法

该方法返回一个以给定值解析后的 Promise 对象。如果这个值是一个 promise,那么将返回这个 promise;如果这个值是 thenable(即带有 “then” 方法),返回的 promise 会“跟随”这个 thenable 的对象,采用它的最终状态;否则返回的 promise 将以此值完成。此函数将类 promise 对象的多层嵌套展平。

语法格式

Promise.resolve(value);

参数

参数 描述
value 将被 Promise 对象解析的参数,也可以是一个Promise 对象,或者是一个 thenable。

返回值

返回一个带着给定值解析过的 Promise 对象,如果参数本身就是一个 Promise 对象,则直接返回这个 Promise 对象。

3. 使用 Typescript 实现 Promise

本小节中,由于 ES6 以后直接为我们提供了 Promise 对象,在我们自己对其进行实现时,不再以 Promise 作为名字,转而使用 Future

3.1 Thenable 接口

事实上 Promise 可以看作时一个 thenable 接口的实现,实现 thenable 的对象也可以称作 Promise Like 对象:

interface Thenable<T> {
  then<TResult1 = T, TResult2 = never>(
    onfulfilled?:
      | ((value: T) => TResult1 | Thenable<TResult1>)
      | undefined
      | null,
    onrejected?:
      | ((reason: any) => TResult2 | Thenable<TResult2>)
      | undefined
      | null
  ): Thenable<TResult1 | TResult2>;
}

3.2 Promise 状态枚举

enum FutureState {
  PENDING = "pending",
  FULFILL = "fulfilled",
  REJECT = "rejected",
}

3.3 Promise 构造器的实现

先声明一个容纳回调的容器接口:

declare type callbacktype = {
  onfulfilled?: ((value: any) => void | Thenable<void>) | null | undefined;
  onrejected?: ((reason: any) => void | Thenable<void>) | null | undefined;
};

下面的代码仅给出 Promise 属性 和 构造器

class Future<T> {
  private _state: FutureState;
  private _result: T | Thenable<T> | undefined;
  private _callbacks: callbacktype[] = [];

  constructor(
    executor: (
      resolve: (value: T | Thenable<T>) => void,
      reject: (reason?: any) => void
    ) => void
  ) {
    // 初始化状态为 PEDDING
    this._state = FutureState.PENDING;

    // 很重要
    // 因为 this 在新定义的函数内会改变指向
    let self = this;

    function _resolve(value: T | Thenable<T>) {
      // 一个 Future 对象的状态只改变一次
      if (self._state === FutureState.PENDING) {
        self._state = FutureState.FULFILL;
        self._result = value;
      }

      // 对于异步任务,成功时回调执行
      self._callbacks.forEach(
        // 多个回调分别执行
        (callback) => {
          if (callback.onfulfilled) {
            callback.onfulfilled(value);
          }
        }
      );
    }

    function _reject(reason?: any) {
      // 一个 Future 对象的状态只改变一次
      if (self._state === FutureState.PENDING) {
        self._state = FutureState.REJECT;
        self._result = reason;
      }

      // 对于异步任务,失败时回调执行
      self._callbacks.forEach((callback) => {
        if (callback.onrejected) {
          callback.onrejected(reason);
        }
      });
    }

    // 调用执行器函数
    try {
      executor(_resolve, _reject);
    } catch (error) {
      _reject(error);
    }
  }
// ...
}

3.4 Promise 的实例 then 方法的实现

then 方法为 Promise 实例方法
Promise.prototype.then()

以下代码仅给出 then 方法:

class Future<T> {
  // ...
   then(
    onfulfilled?: ((value: any) => void | Thenable<void>) | null | undefined,
    onrejected?: ((reason: any) => void | Thenable<void>) | null | undefined
  ) {
    // 返回值也是一个 Future 对象的实例,
    // 并且:
    // - 如果当前层回调不返回 Future:
    //   则实例状态取决于当前回调函数中所改变的状态是 FULFILL 还是 REJECT
    // - 如果前层回调返回的也是 Future 实例,且当前执行器函数中本身为成功:
    //   则当前实例成功还是失败的状态取决于回调函数返回的 Future 实例状态是 FULFILL 还是 REJECT

    // 很重要,确保在新对象里面能由变量指向当前对象
    let self = this;

    // 要能够在没有传递回调时正常地完成链式调用,需要定义默认地回调函数
    // 1. 若 onfulfilled 没有定义
    if (typeof onfulfilled !== "function") {
      onfulfilled = (value) => {
        return value;
      };
    }
    // 2. 若 onrejected 没有定义
    if (typeof onrejected !== "function") {
      onrejected = (reason) => {
        throw reason;
      };
    }

    return new Future((resolve, reject) => {
      // 执行器回调函数,可能是 成功的回调 onfulfilled 也可能是 失败的回调 onrejected
      function callback(callbackType: {
        (value: any): void | Thenable<void>;
        (reason: any): void | Thenable<void>;
        (value: any): void | Thenable<void>;
        (reason: any): void | Thenable<void>;
        (arg0: T | Thenable<T> | undefined): any;
      }) {
        // 获取回调函数的返回值
        let res = callbackType(self._result);
        // 如果回调的返回值类型是 Future 对象实例
        if (res instanceof Future) {
          res.then(
            // 若返回的 Future 接受,则返回接受的返回值(传递 Future 链成功的返回值)
            (returns) => {
              resolve(returns);
            },
            // 若返回的 Future 拒绝,则返回拒绝的原因(传递 Future 链 拒绝和拒绝原因)
            (reason) => {
              reject(reason);
            }
          );
          // 若不是 Future 对象的实例,则返的 Future 进入接受状态
          // 且传递 接受回调(callback 为 onfulfilled时) 的 返回值
        } else {
          resolve(res);
        }
      }

      // 1. FULFILL: 如果成功,则执行成功的回调
      if (self._state === FutureState.FULFILL) {
        // 若调用回调异常也应导致 Future 状态为 REJECT
        try {
          // 异步执行
          setTimeout(() => {
            callback(onfulfilled as (value: any) => void);
          });
        } catch (error) {
          reject(error);
        }
      }
      // 2. REJECT: 如果失败,则执行失败的回调
      else if (self._state === FutureState.REJECT) {
        try {
          setTimeout(() => {
            callback(onrejected as (reason: any) => void | Thenable<void>);
          });
        } catch (error) {
          reject(error);
        }
      }
      // 3. PENDING: 针对于异步任务,状态仍是 PENDING 时需要保存回调函数
      // 执行器执行异步任务中调用 then 方法时,还不知道成功或者失败,
      // 因此只好同时将 成功的回调onfulfilled 和失败的回调onrejected 先同时保存
      // 待到执行器执行完异步任务后,在执行器中调用成功或者失败的回调时、或者直接
      // 因为执行器执行失败,在 then 方法 catch 块调用的 reject() 时,
      // 再执行这些相应的回调
      else if (self._state === FutureState.PENDING) {
        self._callbacks.push({
          onfulfilled: function () {
            try {
              callback(onfulfilled as (value: any) => void | Thenable<void>);
            } catch (error) {
              reject(error);
            }
          },
          onrejected: function () {
            try {
              callback(onrejected as (reason: any) => void | Thenable<void>);
            } catch (error) {
              reject(error);
            }
          },
        });
      } else {
        console.log("Future State is:", self._state);
        throw "FutureStateError : Got an error status.";
      }
    });
  }
  // ...
}

3.5 Promise 的实例 catch 方法的实现

catch 方法为 Promise 实例方法
Promise.prototype.catch()

class Future<T> {

  // ...
  catch<TResult = never>(
    onrejected?:
      | ((reason: any) => TResult | Thenable<TResult>)
      | undefined
      | null
  ): Future<T | TResult> {
    return this.then(undefined, onrejected);
  }
  // ...
}

3.6 Promise 的实例 finally 方法的实现

finally 方法为 Promise 实例方法
Promise.prototype.finally()

class Future<T> {

  // ...
  finally(onfinally?: (() => void) | undefined | null): Future<T> {
    if(typeof onfinally !== "function"){
      onfinally = () => {
        return;
      };
    }
    return this.then(
      (value)=>{
        return Future.resolve((onfinally as (() => void))()).then(
            ()=>value
          )
      },
      (reason)=>{
        return Future.resolve((onfinally as (() => void))()).then(
          ()=>{throw reason}
        )
      }
    )
  }
  // ...

3.7 Promise 的静态 all 方法的实现

all 方法为 Promise 静态方法
Promise.all(iterable)

这个方法返回一个新的 promise 对象,等到所有的 promise 对象都成功或有任意一个 promise 失败。

如果所有的 promise 都成功了,它会把一个包含 iterable 里所有 promise 返回值的数组作为成功回调的返回值。顺序跟 iterable 的顺序保持一致。

一旦有任意一个 iterable 里面的 promise 对象失败则立即以该 promise 对象失败的理由来拒绝这个新的 promise。

class Future<T> {
  // ...
  static all<T>(iterable: Iterable<Future<T>>) {
    let ct = 0;
    let length = 0;
    let arr: any[] = [];
    return new Future((resolve, reject) => {
      for (let future of iterable) {
        length = length + 1;
        future.then(
          (value: any) => {
            ct = ct + 1;
            arr[ct] = value;
          },
          (reason: any) => {
            reject(reason);
          }
        );
      }
      if (length === ct) {
        resolve(arr);
      }
    });
  }
  // ...
}

3.8 Promise 的静态 allSettled 方法的实现

allSettled 方法为 Promise 静态方法
Promise.allSettled(iterable)

等到所有 promise 都已敲定(每个 promise 都已兑现或已拒绝)。

返回一个 promise,该 promise 在所有 promise 都敲定后完成,并兑现一个对象数组,其中的对象对应每个 promise 的结果。

class Future<T> {
  // ...

  // ...
}

3.9 Promise 的静态 any 方法的实现

any 方法为 Promise 静态方法
Promise.any(iterable)

接收一个 promise 对象的集合,当其中的任意一个 promise 成功,就返回那个成功的 promise 的值。

class Future<T> {
  // ...
  static any<T>(iterable: Iterable<Future<T>>) {
    return new Future((resolve, reject) => {
      for (let future of iterable) {
        future.then(
          // 某个 future 兑现则新 future 兑现
          (value: any) => {
            resolve(value);
          },
          // 某个 future 拒绝,不做任何处理
          (reason: any) => {}
        );
      }
    });
  }
  // ...
}

3.10 Promise 的静态 race 方法的实现

race 方法为 Promise 静态方法
Promise.race(iterable)

等到任意一个 promise 的状态变为已敲定。

当 iterable 参数里的任意一个子 promise 成功或失败后,父 promise 马上也会用子 promise 的成功返回值或失败详情作为参数调用父 promise 绑定的相应处理函数,并返回该 promise 对象。

class Future<T> {
  // ...
  race<T>(iterable: Iterable<Future<T>>) {
    return new Future((resolve, reject) => {
      for (let future of iterable) {
        future.then(
          (value: any) => {
            resolve(value);
          },
          (reason: any) => {
            reject(reason);
          }
        );
      }
    });
  }
  // ...
}

3.11 Promise 的静态 reject 方法的实现

reject 方法为 Promise 静态方法
Promise.reject(reason)

返回一个状态为已拒绝的 Promise 对象,并将给定的失败信息传递给对应的处理函数。

class Future<T> {
  // ...
  static reject(reason?: any): Future<any> {
    return new Future<any>((resolve, reject) => {
      reject(reason);
    });
  }
  // ...
}

3.12 Promise 的静态 resolve 方法的实现

resolve 方法为 Promise 静态方法
Promise.resolve(value)

返回一个状态由给定 value 决定的 Promise 对象。如果该值是 thenable(即,带有 then 方法的对象),返回的Promise 对象的最终状态由 then 方法执行结果决定;否则,返回的 Promise 对象状态为已兑现,并且将该 value 传递给对应的 then 方法。

通常而言,如果你不知道一个值是否是 promise 对象,使用 Promise.resolve(value) 来返回一个 Promise 对象,这样就能将该 value 以 promise 对象形式使用。

class Future<T> {
  // ...
  static resolve(): Future<void>; // 重载1
  static resolve<T>(value: T | Thenable<T>): Future<T>;// 重载2
  static resolve<T>(value?: T | Thenable<T>) {
    return new Future((resolve, reject) => {
      if (value instanceof Future) {
        value.then(
          (vl) => {
            resolve(vl);
          },
          (rs) => {
            reject(rs);
          }
        );
      } else {
        resolve(value);
      }
    });
  }
  // ...
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。