Promise浅入浅出

368 阅读5分钟

Promise三种状态

  • pending【等待中】
  • resolved【完成了】
  • rejected【拒绝了】

Promise是JS对于异步方法的实现,解决了传统方法的地狱回调,传统方法的地狱回调会导致代码层层嵌套非常混乱

基本用法

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

通过then方法可以实现Promise链,在then中第一个回调方法是resolve,第二个回调方法是reject,在resolve方法中再返回Promise,则可以继续在外层通过then继续回调

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

reject和catch的区别

  1. 在then回调中,resolve回调方法中报错的话,reject是无法捕获的,reject是决定状态的失败回调,在进入到resolve方法后,就与reject无关了
  2. 如果某个then报错了,会一直保持reject状态,直到进入catch
  3. 在异步回调中报错,不会被catch到
  4. promise状态变化为resolve或者reject后,就不会再改变了,所以在resolve或者reject后抛出错误,是不会触发到catch的
const test = new Promise((resolve, reject) => {
  // throw "error"; // 可以捕获  输出 error
  // resolve(666);
  // throw "error"; // 不能捕获

  setTimeout(() => {
    throw "error"; // 不能捕获
  }, 0);
});

test
  .then((data) => {
    console.log("data1", data);
    // throw "error"; // 可以捕获,先输出666,再输出error
    return Promise.resolve(777);
  })
  .then((data) => {
    console.log("data2", data);
  })
  .catch((err) => {
    console.log("err", err);
  });

Promise.resolve

有时需要将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用。

Promise.resolve等价于下面的写法

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))

1)参数是一个 Promise 实例

如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。

2)参数是一个thenable对象

thenable对象指的是具有then方法的对象,Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});

3)参数不是具有then方法的对象,或根本就不是对象。 如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。

const p = Promise.resolve('Hello');

p.then(function (s){
  console.log(s)
});
// Hello

4)不带有任何参数

Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。

所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用Promise.resolve方法。

const p = Promise.resolve();

p.then(function () {
  // ...
});

Promise.reject

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, function (s) {
  console.log(s)
});
// 出错了

上面代码生成一个 Promise 对象的实例p,状态为rejected,回调函数会立即执行。

注意,Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致。

const thenable = {
  then(resolve, reject) {
    reject('出错了');
  }
};

Promise.reject(thenable)
.catch(e => {
  console.log(e === thenable)
})
// true

上面代码中,Promise.reject方法的参数是一个thenable对象,执行以后,后面catch方法的参数不是reject抛出的“出错了”这个字符串,而是thenable对象。

其他常用API

Promise.all与Promise.allSettled

const test = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(666);
  }, 0);
});

const test2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(777);
  }, 0);
});

const test3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(888);
  }, 0);
});

Promise.allSettled([test, test2, test3]).then((res) => {
  console.log(res);
  // [{status:'fulfilled',value:666},{status:'rejected';,value:777},{status:'fulfilled',value:888}]
});

Promise.all([test, test2, test3]).then(
  (res) => {
    console.log("ok", res);
  },
  (err) => {
    console.log("err", err);
    // err 777
  }
);

Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve)

如果参数中promise有一个失败(rejected),此实例回调失败(reject),失败的原因是第一个失败 promise 的结果。

Promise.allSettled会返回所有结果的数组,无论成功失败,状态通过数组中对象的status属性体现

Promise.any(截止当前,该方法处于stage4阶段,会在明年进入正式语法)

只要有一个resolve了,则返回结果,如果一个都没有resolve,则返回失败

const pErr = new Promise((resolve, reject) => {
  reject("总是失败");
});

const pSlow = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, "最终完成");
});

const pFast = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "很快完成");
});

Promise.any([pErr, pSlow, pFast]).then((value) => {
  console.log(value);
  // pFast fulfils first
})
// 期望输出: "很快完成"

Promise.race

只要任意一个结果返回resolve或者reject,就立即返回结果

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});

Promise.race([promise1, promise2]).then((value) => {
  console.log(value);
  // Both resolve, but promise2 is faster
});
// expected output: "two"

手写原理

手写实现Promise基础版

// 手写实现Promise
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";

function MyPromise(fn) {
  const that = this;
  that.state = PENDING;
  that.value = null;
  that.resolvedCallbacks = [];
  that.rejectedCallbacks = [];

  function resolve(value) {
    if (that.state === PENDING) {
      that.state = RESOLVED;
      that.value = value;
      that.resolvedCallbacks.map((cb) => {
        cb(value);
      });
    }
  }
  function reject(value) {
    if (that.state === PENDING) {
      that.state = REJECTED;
      that.value = value;
      that.rejectedCallbacks.map((cb) => {
        cb(value);
      });
    }
  }

  try {
    fn(resolve, reject);
  } catch (e) {
    reject(e);
  }
}

MyPromise.prototype.then = function (onFulfilled, onRejected) {
  const that = this;
  onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : (r) => {
          throw r;
        };
  if (that.state === PENDING) {
    that.resolvedCallbacks.push(onFulfilled);
    that.rejectedCallbacks.push(onRejected);
  }
  if (that.state === RESOLVED) {
    onFulfilled(that.value);
  }
  if (that.state === REJECTED) {
    onRejected(that.value);
  }
};

手写实现Promise.all

Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    let index = 0;
    const res = [];
    if (!promises.length) {
      resolve(res);
    } else {
      function processValue(i, data) {
        res[i] = data;
        if (++index === promises.length) {
          resolve(res);
        }
      }

      for (let i = 0; i < promises.length; i += 1) {
        if (!promises[i] instanceof Promise) {
          processValue(i, promises[i]);
        } else {
          promises[i].then(
            (data) => {
              processValue(i, data);
            },
            (err) => {
              reject(err);
            }
          );
        }
      }
    }
  });
};

手写实现Promise.finally

Promise.finally = function (callback) {
  return this.then(
    (value) => {
      return Promise.resolve(callback()).then(() => {
        return value;
      });
    },
    (err) => {
      return Promise.resolve(callback()).then(() => {
        throw err;
      });
    }
  );
};

手写实现Promise.race

Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i += 1) {
      if (!promises[i] instanceof Promise) {
        Promise.resolve(promises[i]).then(resolve, reject);
        return;
      } else {
        promises[i].then(resolve, reject);
        return;
      }
    }
  });
};