Skip to content

promise

什么是 promise

  • Promise 是一种在 JavaScript 中提供了异步编程范式的特殊对象。它可以被用来表示一个将来会发生的操作,或者说它是一个代表一个异步操作结果。使用它,可以轻松地实现异步请求,并用非常方便的方式处理请求成功或失败之后的操作

promise 优缺点

  • 优:promise 很好的解决了异步编程中的,回调层层嵌套问题,使代码结构看起来更加的简洁,可读性更强
  • 缺:promise 一旦开始,无法取消或重置,意味着犯错了无法挽回

promiseA+规范

  • 所有的 promise 都会遵循一个 promiseA+规范来实现promiseA+文档

1、promise 执行器

  • promise 是一个类,当在实例化时,会立马执行构造器,构造器接受 resolve, reject 函数等待用户执行

    WARNING

    构造器在执行的时候如果出现异常直接会调用 reject 函数

js
class Promise {
  constructor(executor) {
    const resolve = (value) => {
      console.log(value);
    };
    const reject = (reason) => {
      console.log(reason);
    };
    try {
      // 出现异常直接走reject
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
}

new Promise((resolve, reject) => {
  resolve("ok");
});

new Promise((resolve, reject) => {
  throw "error";
  resolve("ok");
});

2、promise 状态

  • promise 有种状态分别是pending(等待)fufilled(成功)rejected(失败)
  • 状态只能从pending=>fufilled或者pending=>rejected不能逆转
  • 当在执行构造器中的resolve或者reject时,如果此时的状态是 pending,改变当前的状态,并且把成功或者失败的回调值存储在实例上
js
const STATUS = {
  PENDING: "PENDING",
  FUFILLED: "FUFILLED",
  REJECTED: "REJECTED",
};

class Promise {
  constructor(executor) {
    this.status = STATUS.PENDING;
    this.value = undefined;
    this.reason = undefined;
    const resolve = (value) => {
      if (this.status === STATUS.PENDING) {
        this.status = STATUS.FUFILLED;
        this.value = value;
      }
    };
    const reject = (reason) => {
      if (this.status === STATUS.PENDING) {
        this.status = STATUS.REJECTED;
        this.reason = reason;
      }
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
}

new Promise((resolve, reject) => {
  resolve("ok");
});

new Promise((resolve, reject) => {
  reject("error");
});

3、then 函数

  • 1、then 接收 2 个回调函数(onFulfilled,onRejected),一个成功的回调,一个失败的回调
  • 2、当 then 回调传递的不是函数时,为了能够把值传递下去,会转换成函数
  • 3、当在调用 then 函数时,如果此次时的状态是 pending(等待),会把成功、失败的回调加入到队列中,等待执行
  • 4、如果此时的状态不是 pending(等待),会直接调用成功或者失败的回调
js
const STATUS = {
  PENDING: "PENDING",
  FUFILLED: "FUFILLED",
  REJECTED: "REJECTED",
};

class Promise {
  constructor(executor) {
    this.status = STATUS.PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    const resolve = (value) => {
      if (this.status === STATUS.PENDING) {
        this.status = STATUS.FUFILLED;
        this.value = value;
        this.onFulfilledCallbacks.forEach((onRejected) => {
          onRejected(this.value);
        });
      }
    };
    const reject = (reason) => {
      if (this.status === STATUS.PENDING) {
        this.status = STATUS.REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((onRejected) => {
          onRejected(this.value);
        });
      }
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
  then(onFulfilled, onRejected) {
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (err) => {
            throw err;
          };
    if (this.status === STATUS.PENDING) {
      this.onFulfilledCallbacks.push(() => onFulfilled);
      this.onRejectedCallbacks.push(() => onRejected);
    } else if (this.status === STATUS.FUFILLED) {
      onFulfilled(this.value);
    } else {
      onRejected(this.reason);
    }
  }
}

let promise = new Promise((resolve, reject) => {
  resolve("ok");
});
promise.then((value) => {
  console.log(value);
});

new Promise((resolve, reject) => {
  reject("error");
});

4、then 函数链式调用

  • 为了支持 then 能够链式调用,then 函数会返回一个新的 promise2
js
  then ( onFulfilled, onRejected ) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : ( value ) => value
    onRejected = typeof onRejected === 'function' ? onRejected : ( err ) => { throw err };

    const promise2 = new Promise( ( resolve, reject ) => {
      if ( this.status === STATUS.PENDING ) {
        this.onFulfilledCallbacks.push( () => onFulfilled )
        this.onRejectedCallbacks.push( () => onRejected )
      } else if ( this.status === STATUS.FUFILLED ) {
        onFulfilled( this.value )
      } else {
        onRejected( this.reason )
      }
    } )
    return promise2

  }

5、then 函数回调返回值处理

  • 如果 then 回调函数调用出现异常,会走到下一个失败的 then 回调中
  • 如果 then 回调返回的是一个 promise,promise 成功或者失败,会走到下一个成功或者失败的 then 回调中
  • 如果 then 回调返回的是一个普通的值,不管是成功或者失败的回调,都有走到下一个成功的 then 回调中

循环引用案例

js
// 循环引用案例:
const promise2 = new Promise((resolve, reject) => {
  resolve("ok");
});
// 循环引用,造成无限死循环需要处理成 reject失败
promise2.then(() => {
  return promise2;
});

// 处理异常
if (promise2 === x) {
  return reject(new TypeError("循环引用,造成无限死循环"));
}
// ....

then 回调返回值是对象,取 then 异常处理

js
const x = {};

Object.defineProperty(x, "then", {
  get() {
    throw new Error("error");
  },
});

// 处理异常
try {
  const then = x.then;
} catch (e) {
  // 取then错误走下一个失败的then回调
  reject(e);
}
// ....

如果 then 回调返回值是一个 Promise,并且 resolve 中的值也是一个 Promise,需要递归把值传递下去

js
const promise = new Promise((resolve, reject) => {
  resolve("ok");
});
promise.then(() => {
  return new Promise((resolve, reject) => {
    resolve(
      Promise((resolve, reject) => {
        resolve("ok");
      })
    );
  });
});

// 处理递归
then.call(
  x,
  (y) => {
    // y有可能还是一个promise,递归到普通值结束
    resolvePromise(y, promise2, resolve, reject);
  },
  (r) => {
    // promise失败直接到下一个失败的then回调
    reject(r);
  }
);
// ....

如果 then 回调返回值是不一个规范的 Promise,即可以成功也可以失败,我们需要防止这种情况,不可以逆向

js
const promise = new Promise((resolve, reject) => {
  resolve("ok");
});
promise.then(() => {
  // 自己实现的Promise即可以成功也可以失败
  return new MyPromise((resolve, reject) => {
    resolve("ok");
    reject("err");
  });
});

// 处理不规范的Promise(即可以成功也可以失败)

let called;
try {
  const then = x.then;
  if (typeof then === "function") {
    // 执行promise.then
    then.call(
      x,
      (y) => {
        if (called) return;
        called = true;
        // y有可能还是一个promise,递归到普通值结束
        resolvePromise(y, promise2, resolve, reject);
      },
      (r) => {
        if (called) return;
        called = true;
        // promise失败直接到下一个失败的then回调
        reject(r);
      }
    );
  } else {
    if (called) return;
    called = true;
    // 普通值走下一个成功的then回调
    resolve(x);
  }
} catch (e) {
  if (called) return;
  called = true;
  // 取then错误走下一个失败的then回调
  reject(e);
}
// ....

6、完整源码实现

js
const STATUS = {
  PENDING: "PENDING",
  FUFILLED: "FUFILLED",
  REJECTED: "REJECTED",
};

/**
 *
 * @param {*} x then回调的返回值
 * @param {*} promise promise2的实例
 * @param {*} resolve promise成功
 * @param {*} reject promise失败
 */

function resolvePromise(x, promise2, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError("循环引用,造成无限死循环"));
  }
  // 对象或者function都有可能是Promise
  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    // 防止别人实现的promise即可以成功,也可以失败
    let called;
    try {
      const then = x.then;
      if (typeof then === "function") {
        // 执行promise.then
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            // y有可能还是一个promise,递归到普通值结束
            resolvePromise(y, promise2, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            // promise失败直接到下一个失败的then回调
            reject(r);
          }
        );
      } else {
        if (called) return;
        called = true;
        // 普通值走下一个成功的then回调
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      // 取then错误走下一个失败的then回调
      reject(e);
    }
  } else {
    // 普通值走下一个成功的then回调
    resolve(x);
  }
}

class Promise {
  constructor(executor) {
    this.status = STATUS.PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    const resolve = (value) => {
      if (this.status === STATUS.PENDING) {
        this.status = STATUS.FUFILLED;
        this.value = value;
        this.onFulfilledCallbacks.forEach((onRejected) => {
          onRejected(value);
        });
      }
    };
    const reject = (reason) => {
      if (this.status === STATUS.PENDING) {
        this.status = STATUS.REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((onRejected) => {
          onRejected(reason);
        });
      }
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }

  then(onFulfilled, onRejected) {
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (err) => {
            throw err;
          };

    const promise2 = new Promise((resolve, reject) => {
      if (this.status === STATUS.PENDING) {
        this.onFulfilledCallbacks.push(() => {
          // 保证promise2实例化完成
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value);
              // 处理回调的结果
              resolvePromise(x, promise2, resolve, reject);
            } catch (e) {
              // then回调出新异常直接调用下一个失败的then
              reject(e);
            }
          });
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason);
              resolvePromise(x, promise2, resolve, reject);
            } catch (e) {
              reject(e);
            }
          });
        });
      } else if (this.status === STATUS.FUFILLED) {
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            resolvePromise(x, promise2, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      } else {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason);
            resolvePromise(x, promise2, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      }
    });
    return promise2;
  }

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

  static resolve(val) {
    return new Promise((resolve) => {
      resolve(val);
    });
  }

  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason);
    });
  }

  static all(promisArr) {
    return new Promise((resolve, reject) => {
      let result = [],
        count = 0;

      function done(i, data) {
        result[i] = data;

        if (count++ == i) {
          resolve(result);
        }
      }
      promisArr.map((p, i) => {
        p.then((data) => done(i, data), reject);
      });
    });
  }

  static race(promisArr) {
    return new Promise((resolve, reject) => {
      promisArr.map((p) => {
        p.then((data) => resolve(data), reject);
      });
    });
  }
}

Promise.defer = Promise.deferred = function () {
  let dfd = {};
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
};
module.exports = Promise;

6、case

  • 安装npm install promises-aplus-tests -g
  • 执行promises-aplus-tests promise.js(promise 入口文件路径)

promise-case

then 交替执行

  • 如果有多个 fulfilled 状态的 promise 实例,同时执行 then 链式调用,then 会交替调用
  • 这是编译器的优化,防止一个 promise 持续占据事件
js
Promise.resolve()
  .then(() => {
    console.log(1);
  })
  .then(() => {
    console.log(2);
  })
  .then(() => {
    console.log(3);
  })
  .then(() => {
    console.log(4);
  });

Promise.resolve()
  .then(() => {
    console.log(10);
  })
  .then(() => {
    console.log(20);
  })
  .then(() => {
    console.log(30);
  })
  .then(() => {
    console.log(40);
  });

Promise.resolve()
  .then(() => {
    console.log(100);
  })
  .then(() => {
    console.log(200);
  })
  .then(() => {
    console.log(300);
  })
  .then(() => {
    console.log(400);
  });
// 1 10 100 2 20 200 3 30 300 4 40 400

then 返回 promise

js
Promise.resolve()
  .then(() => {
    console.log(0);
    return Promise.resolve(4); // 相当于多处一个 promise 实例,会慢2拍
  })
  .then((res) => {
    console.log(res);
  });

Promise.resolve()
  .then(() => {
    console.log(1);
  })
  .then(() => {
    console.log(2);
  })
  .then(() => {
    console.log(3);
  })
  .then(() => {
    console.log(5);
  })
  .then(() => {
    console.log(6);
  });
// 0 1 2 3 4 5 6