这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战
前面写了Promise的入门和相关方法的使用,Promise的关键问题,需要的童鞋请移步。这篇文章写下Promise的核心原理:
- Promise的构造函数
- 初始化数据
- 实现resolve方法
- 实现reject方法
- 执行器抛出异常也需要返回错误信息
一、Promise构造函数
在定义promise时都使用new关键字来,很明显promise是一个构造函数。 因此可以得出一个最简单的壳子:
function Promise1() {}
在使用promise时总会传入一个方法,我们称之为执行器executor。
执行器包含resolve和reject两个参数。
resolve和reject是定义在promise构造函数中的两个方法。
function Promise1(executor){
function resolve() {}
function reject() {}
executor(resolve, reject);
}
二、初始化数据
由于promise的三个状态比较常用,所以我们将三个状态的字符串设置为常量,方便后面使用。
var PENDING = "pending";
var FULFILLED = "fulfilled";
var REJECTED = "rejected";
promise构造函数中准备一些变量:
- 变量status来存储promise的状态,初始状态为pending;(Promise 的三种状态:pending-等待,fulfilled-成功,reject-失败,其中最开始为 pending 状态,并且一旦成功或者失败,Promise 的状态便不会再改变)
- 变量data来存储promise 返回的结果;
- 变量callbacks来存储then设置的两个回调,每个元素的结构:{ onResolved(){}, onRejected(){} }。这里用数组来存储是为了满足一个promise能设置多个成功/失败回调函数。
function Promise1(executor){
var _this = this;
_this.status = PENDING;
_this.data = undefined;
_this.callbacks = [];
}
三、实现resolve方法
resolve内部会做哪些事情呢?
- 由于promise的状态只能由pending变为fulfilled/rejected,所以要先判断状态是否为pending,若不是则返回,若是则继续执行;
- 将promise状态改为fulfilled;
- 将传入的数据保存起来,以便后面使用;
- 判断callbacks中是否有内容,若有,则执行其中的onResolved方法,由于回调函数必须是异步执行,所以使用setTimeout方法来实现异步执行。
为什么需要使用callbacks来存储回调函数?
因为当用户先指定回调函数时,promise的状态并没有改变,还无法执行回调函数,所以要先将回调函数存起来,等状态改变后再调用。
- 当用户先指定回调函数再改变状态时,callbacks中才会存入指定的回调函数,这个操作在then方法中会实现。
- 当用户先改变状态再指定回调函数时,callbacks中是不会存储数据的;
function reslove(value) {
if (_this.status != PENDING) return;
_this.status = FULFILLED;
_this.data = value;
if (_this.callbacks.length > 0) {
setTimeout(function() {
_this.callbacks.forEach(function(callbackObj) {
callbackObj.onResolved(value);
});
});
}
}
四、实现reject方法
reject内部操作与resolve的接近一致,只是状态和回调函数不同。
- 由于promise的状态只能由pending变为fulfilled/rejected,所以要先判断状态是否为pending,若不是则返回,若是则继续执行;
- 将状态改为rejected;
- 将传入的数据保存起来,以便后面使用;
- 判断callbacks中是否有内容,若有,则执行其中的onRejected方法,由于回调函数必须是异步执行,所以使用setTimeout方法来实现异步执行。
function reject(reason) {
if (_this.status != PENDING) return;
_this.status = REJECTED;
_this.data = reason;
if (_this.callbacks.length > 0) {
setTimeout(function() {
_this.callbacks.forEach(function(callbackObj) {
callbackObj.onRejected(reason);
});
});
}
}
五、执行器抛出异常也需要返回错误信息
在Promise的关键问题文中提到改变promise状态的三种方式:
- resolve(value): 如果当前是pending状态则变为fulfilled状态;
- reject(reason): 如果当前是pending状态则变为rejected状态;
- 抛出异常:如果当前是pending状态则变为rejected状态。
执行器在执行过程中可能出现错误,所以当抛出异常时,需要捕获异常并将promise状态改为rejected。因此需要在执行器外面包上一层捕获异常的方法,并在出错时调用reject方法。
try {
executor(reslove, reject);
} catch (error) {
reject(error);
}