promise 知识点总结

191 阅读2分钟

promise 是一种处理异步任务和 callback 函数的解决方案。promise 解决了地狱回调问题(多个 callbacks 嵌套在一起),增强了代码可读性。

创建一个 promise

let promise = new Promise(function(resolve, reject){
    // executor
})

new Promise() 中传入的函数叫 executor,将异步任务放在 executor 里。promise 一旦被创建,executor 便立刻同步执行。

resolve 和 reject 是内置的回调函数,如果异步任务执行成功,调用 resolve(value); 如果发生错误就调用 reject(error)

一个 promise 对象有两个内置属性:

  • state: 初始为 "pending", 调用 resolve(value) 变为 "fulfilled", 调用 reject(error) 变为 "rejected"。state 只能更改一次。
  • result: 初始为 "undefined", 调用 resolve(value) 变为 "value", 调用 reject(error) 变为 "error"。result 只能更改一次。

使用 Promise.prototype.then

promise.then(
    result => {
    // 处理成功结果
    },
    error => {
    // 处理错误
    }
)

promise.then 函数中可传入两个函数,第一个函数 onFulfilled 处理成功结果,第二个函数 onError 处理错误。也可以只传入第一个成功函数。

promise.then() 返回一个 promise, 所以可以链式调用 then 函数。

  • 返回的新 promise 取决于传入 then 中回调函数的返回值;

  • 回调函数返回普通值,新 promise 中 state 变为 "fulfilled",result 为返回的值;

  • 回调函数产生 error,新 promise 中 state 变为 "rejected",result 为 error;

  • 回调函数返回一个新 promise,then 返回的 promise 会等回调函数返回的promise先执行且属性取决于新返回的 promise;

使用 Promise.all

let promise = Promise.all([promise1, promise2, ...])

Promise.all 接收一个包含多个 promise 的列表,同时执行多个 promises,然后返回一个新 promise。

如果所有 promise 都成功地执行完毕,返回的新 promise state 变为 "fulfilled",result 就是包含多个 promises 结果的列表。如果其中任何一个 promise 产生错误,Promise.all 立刻返回一个 rejected 的 promise,其他 promises 的结果会被无视。

使用 Promise.race

和 Promise.all 类似,但只返回第一个执行完毕的 promise (不论成功与失败)。

手写一个简易 Promise

class myPromise {
  constructor(fn) {
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    this.status = 'pending';
    this.value = null;
    this.error = null;

    let resolve = value => {
      let fn = () => {
        if (this.status !== 'pending') return;
        this.status = 'fulfilled';
        this.value = value;
        this.onFulfilledCallbacks.forEach(cb => cb(this.value));
      };
      setTimeout(fn, 0);
    };

    let reject = error => {
      let fn = () => {
        if (this.status !== 'pending') return;
        this.status = 'rejected';
        this.error = error;
        this.onRejectedCallbacks.forEach(cb => cb(this.error));
      };
      setTimeout(fn, 0);
    };
    fn(resolve, reject);
  }
  then(onFulfilled, onRejected) {
    return new myPromise((resolve, reject) => {
      const fulfilled = value => {
        try {
          if (typeof onFulfilled !== 'function') {
            resolve(value);
          } else {
            const ret = onFulfilled(value);
            if (ret instanceof myPromise) {
              ret.then(resolve, reject);
            } else {
              resolve(ret);
            }
          }
        } catch (err) {
          reject(err);
        }
      };
      const rejected = error => {
        try {
          if (typeof onRejected !== 'function') {
            reject(error);
          } else {
            const ret = onRejected(error);
            if (ret instanceof myPromise) {
              ret.then(resolve, reject);
            } else {
              resolve(ret);
            }
          }
        } catch (err) {
          reject(err);
        }
      };
      switch (this.status) {
        case 'pending':
          this.onFulfilledCallbacks.push(onFulfilled);
          this.onRejectedCallbacks.push(onRejected);
          break;
        case 'fulfilled':
          setTimeOut(fulfilled(this.value));
          break;
        case 'rejected':
          setTimeOut(rejected(this.value));
          break;
      }
    });
  }
  catch(onRejected) {
    return this.then(null, onRejected);
  }
  // 无论成功和失败都执行 cb
  finally(cb) {
    return this.then(cb, cb);
  }

  static resolve(val) {
    if (val instanceof myPromise) return val;
    return new myPromise(res => res(val));
  }
  static reject(err) {
    return new myPromise((res, rej) => rej(err));
  }
  static all(promises) {
    return new myPromise((res, rej) => {
      let result = [];
      let count = 0;
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(
          val => {
            result[i] = val;
            count += 1;
            if (count === promises.length) {
              res(result);
            }
          },
          err => {
            rej(err);
          }
        );
      }
    });
  }
  static race(promises) {
    return new myPromise((res, rej) => {
      for (let i = 0; i < promises.length; i++) {
        myPromise.resolve(promises[i]).then(res, rej);
      }
    });
  }
}