你可能不知道的 Promise

本文介绍了 Promise 的相关知识,包括其三种状态及状态改变规则。还详细阐述了 then、catch、finally 等方法,以及 Promise.all()、Promise.race() 等静态方法的功能和使用方式。此外,提到了 Promise.try() 和 Promise 与 Generator 的结合,最后补充了简单实现。

你可能不知道的 Promise

参考

简介

  • Promise 有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)
  • 只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态
  • 一旦状态改变,就不会再变
  • 状态只能 pending->fulfilled/rejected
  • 如果状态改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果

then

  • 接受两个方法参数,第一个参数是 resolved 状态的回调函数,第二个参数(可选)是 rejected 状态的回调函数
  • then 方法返回的是一个新的 Promise 实例(注意,不是原来那个 Promise 实例)
  • then 方法可以采用链式回调,后一个 then 接收的参数是前一个的返回值
  • 后一个 then 的状态取决于前一个 then 返回的状态
var promise = new Promise((resolve, reject) => {
  reject(1)
})
promise.then(null, data => {
  console.log(data) // 1
})
promise.then(null, data => {
  console.log(data) // 1
})

// 第一个执行的是 rejected,第二个执行的是 resolved
new Promise((resolve, reject) => {
  reject(1)
})
  .then(null, data => {
    console.log(data) // 1
  })
  .then(data => {
    console.log(data) // undefined
  })

new Promise((resolve, reject) => {
  reject(1)
})
  .then(null, data => {
    console.log(data) // 1
    return Promise.reject(data + 1)
  })
  .then(null, data => {
    console.log(data) // 2
  })

catch

  • 指定发生错误时的回调函数
  • rejected 状态或者 then 方法处理数据出错都会触发
  • 拥有 then 的后三种状态
new Promise((resolve, reject) => {
  reject(1)
})
  .catch(data => {
    console.log(data) // 1
    return Promise.reject(data + 1)
  })
  .then(null, data => {
    console.log(data) // 2
  })

new Promise((resolve, reject) => {
  resolve(1)
})
  .catch(data => {
    console.log(data) // 1
  })
  .catch(data => {
    console.log(data) // 不执行
  })

new Promise((resolve, reject) => {
  resolve(1)
})
  .catch(data => {
    console.log(data) // 1
  })
  .then(data => {
    console.log(data) // undefined
  })

finally (ES2018 引入)

  • 指定不管 Promise 对象最后状态如何,都会执行的操作
  • 不接受任何参数
  • finally 方法总是会返回原来的值,也就是在你的链式调用里边加不加它都不会影响原本的流程。在上述代码实例中任何一个位置添加 finally 都不会影响原本的输出
const loading = true
http.get('/xxx').then(
  data => {
    // 处理
    loading = false
  },
  data => {
    // 处理
    loading = false
  }
)

const loading = true
http
  .get('/xxx')
  .then(
    data => {
      // 处理
    },
    data => {
      // 处理
    }
  )
  .finally(() => {
    loading = false
  })

Promise.all()

var p = Promise.all([p1, p2])

  • 将 p1, p2,包装成一个新的 Promise 实例 p
  • 接受一个数组作为参数,参数可以试 Promise,也可以不是。不是的时候会自动转换为 Promise
  • 当 p1, p2 都返回 resolve,p 才会 resolve。p 的接收值是 p1, p2 返回值组成的一个数组
  • 当 p1, p2 其中有一个 reject,p 就会返回 reject。p 的接收值是 p1, p2 中第一个 reject 的返回值

Promise.race()

Promise.all 类似,不同的是只要 p1, p2 其中有一个的状态发生变化,p 的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数。

Promise.resolve()

  • 将现有对象转为 Promise 对象
  • 参数是 Promise 实例,那么 Promise.resolve 将不做任何修改、原封不动地返回这个实例
  • 参数是一个 thenable 对象,thenable 对象指的是具有 then 方法的对象。Promise.resolve 方法会将这个对象转为 Promise 对象,然后就立即执行 thenable 对象的 then 方法
  • 如果参数是一个原始值,或者是一个不具有 then 方法的对象,则 Promise.resolve 方法返回一个新的 Promise 对象,状态为 resolved。并且将其接收的参数传递下去
  • 如果没有参数,则 Promise.resolve 方法返回一个新的 Promise 对象,状态为 resolved。

Promise.reject()

  • 返回一个新的 Promise 实例,该实例的状态为 rejected
  • 将接收的参数原封不动地作为后续方法的参数

Promise.try()

  • 不知道或者不想区分,函数 f 是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管 f 是否包含异步操作,都用 then 方法指定下一步流程,用 catch 方法处理 f 抛出的错误。
  • Promise.try 就是模拟 try 代码块,就像 promise.catch 模拟的是 catch 代码块

Promise + Generator

实现类似 async/await 功能,实际上他就是通过这两个结合实现的

const g = function*() {
  const foo = yield new Promise(function(resolve, reject) {
    resolve('foo')
  })
  console.log(foo)
}
function run(generator) {
  const it = generator()
  function go(result) {
    if (result.done) return result.value
    return result.value.then(
      function(value) {
        return go(it.next(value))
      },
      function(error) {
        return go(it.throw(error))
      }
    )
  }
  go(it.next())
}
run(g)

2020-09-01 补充简单的实现

参考链接

function isPromise(o) {
  return o && typeof o.then === "function";
}

function rejectFactory(value) {
  const run = () => {
    if (this.status !== "pending") return;
    this.status = "rejected";
    this.value = value;
    this.rejectedQueue.forEach((rejected) => rejected(this.value));
  };
  setTimeout(run, 0);
}

function resolveFactory(value) {
  const run = () => {
    if (this.status !== "pending") return;
    this.status = "fulfilled";
    this.value = value;
    if (isPromise(value)) {
      this.value.then(
        (v) => this.resolvedQueue.forEach((resolved) => resolved(v)),
        (e) => this.rejectedQueue.forEach((rejected) => rejected(e))
      );
    } else {
      this.resolvedQueue.forEach((resolved) => resolved(this.value));
    }
  };
  setTimeout(run, 0);
}

class P {
  constructor(resolver) {
    this.value = undefined;
    this.status = "pending";
    this.resolvedQueue = [];
    this.rejectedQueue = [];
    try {
      resolver(resolveFactory.bind(this), rejectFactory.bind(this));
    } catch (error) {
      rejectFactory.call(this, error);
    }
  }

  then(onResolve, onReject) {
    if (!onResolve) onResolve = (o) => o;
    if (!onReject) onReject = (o) => P.reject(o);
    return new P((resolve, reject) => {
      function next(type) {
        return (value) => {
          try {
            const ans = (type === 1 ? onResolve : onReject)(value);
            if (isPromise(ans)) {
              ans.then(resolve, reject);
            } else {
              resolve(ans);
            }
          } catch (error) {
            reject(error);
          }
        };
      }
      if (this.status === "pending") {
        this.resolvedQueue.push(next(1));
        this.rejectedQueue.push(next());
      } else if (this.status === "fulfilled") {
        next(1)(this.value);
      } else {
        next()(this.value);
      }
    });
  }

  catch(onRejected) {
    return this.then(undefined, onRejected);
  }

  finally(onFinally) {
    if (!onFinally) onFinally = () => {};
    if (this.status === "pending") {
      this.resolvedQueue.push(onFinally);
      this.rejectedQueue.push(onFinally);
    } else {
      onFinally();
    }
    return this;
  }

  static resolve(value) {
    return new P((resolve) => resolve(value));
  }

  static reject(value) {
    return new P((resolve, reject) => reject(value));
  }

  static all(iterable) {
    return new Promise((resolve, reject) => {
      let len = iterable.length;
      let values = new Array(len);
      let count = 0;
      if (len === 0) {
        resolve(values);
        return;
      }
      iterable.forEach((item, i) => {
        if (isPromise(item)) {
          item.then(
            (value) => {
              count += 1;
              values[i] = value;
              if (count === len) resolve(values);
            },
            (error) => {
              reject(error);
            }
          );
        } else {
          count += 1;
          values[i] = item;
          if (count === len) resolve(values);
        }
      });
    });
  }

  static race(iterable) {
    return new Promise((resolve, reject) => {
      iterable.forEach((item) => {
        if (isPromise(item)) {
          item.then(
            (value) => resolve(value),
            (error) => reject(error)
          );
        } else {
          resolve(item);
        }
      });
    });
  }

  static any(iterable) {
    return new Promise((resolve, reject) => {
      let len = iterable.length;
      let count = 0;
      if (len === 0) {
        reject("AggregateError: All promises were rejected");
        return;
      }
      iterable.forEach((item, i) => {
        if (isPromise(item)) {
          item.then(
            (value) => {
              resolve(value);
            },
            () => {
              count += 1;
              if (count === len) {
                reject("AggregateError: All promises were rejected");
              }
            }
          );
        } else {
          resolve(item);
        }
      });
    });
  }

  static allSettled(iterable) {
    return new Promise((resolve) => {
      let len = iterable.length;
      let count = 0;
      let values = new Array(len);
      if (len === 0) {
        resolve(values);
        return;
      }
      iterable.forEach((item, i) => {
        if (isPromise(item)) {
          item.then(
            (value) => {
              count += 1;
              values[i] = {
                status: "fulfilled",
                value,
              };
              if (count === len) resolve(values);
            },
            (reason) => {
              count += 1;
              values[i] = {
                status: "rejected",
                reason,
              };
              if (count === len) resolve(values);
            }
          );
        } else {
          count += 1;
          values[i] = {
            status: "fulfilled",
            value: item,
          };
          if (count === len) resolve(values);
        }
      });
    });
  }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值