基本概念
Promise是异步编程的一种解决方案,比传统的解决方案------回调函数和事件------更合理和更强大。所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,同时promise必须遵守Promises/A+规范。
基本术语
-
promise
是带有then方法的对象或函数,其行为符合此规范。 -
thenable
是定义then方法的对象或函数。 -
value
是任何合法的JavaScript值(包括undefined、thenable或promise)。 -
exception
是使用throw语句抛出的值。 -
reason
是一个值,表示promise被拒绝的原因。
Promise状态(Promise States)
-
根据
Promises/A+
规范原文描述,promise
的状态有且只有三种:pending(等待态)
、fulfilled(成功态)
、rejected(失败态)
pending
态时,可以改变其状态为fulfilled
或rejected
。- 其中
fulfilled
和rejected
时状态是不可被改变的,这意味着状态是不可逆的。
-
在
promise
处于pending
态时,能够改变promise
状态的方法有三种:- 调用
resolve
把状态改为fulfilled
; - 调用
reject
把状态改为rejected
; - 抛出错误
thow new Error()
,把状态改为rejected
;
- 调用
直接冻手
- 在代码中,会尽量给
家人们
标记好代码在Promises/A+
的具体说明位置,方便结合规范食用。
第一步
- 定义
promise
的三种状态 - 定义成功原因value
- 定义失败原因reason
promise
在创建时,会接收一个functionexecutor
作为参数,并且会默认执行一次此参数,同时返回内部定义的resolve
和reject
方法供外部修改状态。- 实现一个
then
函数,在promise
状态被修改时,需要调用对应的函数,接收两个参数:- onFulfilled 成功后执行的回调。
- onRejected 失败后执行的回调。
js
const PENDING = 'PENDING'; // 默认态
const FULFILLED = 'FULFILLED'; // 成功态
const REJECTED = 'REJECTED'; // 失败态
class Promise1 {
constructor(executor) {
this.state = PENDING; // 状态
this.reason = null; // 失败原因
this.value = null; // 成功数据
const resolve = (value) => {
// pending状态时,状态才可变更
if (this.state === PENDING) { // 2.1.2.1 must not transition to any other state.
this.state = FULFILLED;
this.value = value; // 2.1.2.2 must have a value, which must not change.
}
};
const reject = (reason) => {
// pending状态时,状态才可变更
if (this.state === PENDING) { // 2.1.3.1 must not transition to any other state.
this.state = REJECTED;
this.reason = reason; // 2.1.3.2 must have a reason, which must not change.
}
};
// 能够改变promise状态的有resolve,reject和抛出异常throw new Error();
try {
executor(resolve, reject); // 默认会执行一次, 同时返回内部定义的resolve和reject方法供外部修改状态。
} catch (error) {
// 如果运行报错,会直接当作失败处理
reject(error);
}
}
then(onFulfilled, onRejected) {
// 2.2 A promise must provide a then method to access its current or eventual value or reason.
// A promise's then method accepts two arguments: onFulfilled AND onRejected
if (this.state === FULFILLED) {
// 当前状态为成功态调用onFulfilled
onFulfilled(this.value);
} else if (this.state === REJECTED) {
// 当前状态为失败态调用onRejected
onRejected(this.reason);
}
}
}
- 我们进行下简单的测试
js
const promise = new Promise1((resolve, reject) => {
resolve('ok');
}).then(data => {
console.log(data); // ok
});
const promise = new Promise1((resolve, reject) => {
reject('fail');
}).then(data => {}, error => {
console.log(error); // fail
});
const promise = new Promise1((resolve, reject) => {
resolve('ok');
reject('fail'); // 这里将不被执行
}).then(data => {
console.log(data); // ok
}, error => {
console.log(error); // 不打印
});
第二步
众所周知promise
是解决回调地狱的,因此其使用场景的执行是异步的,第一步then
被调用时,promise
仍处于pending
态,很显然是无法解决的。
要解决异步调用的问题,需要利用观察者模式,可以在另一篇文章中获得更详细的答案,这里就不详细赘述了。
- 定义
onResolvedCallBacks
缓存所有onFulfilled
观察者函数; - 定义
onRejectedCallBacks
缓存所有的onRejected
观察者函数; then
方法中增加判断pending
逻辑;resolve
被调用时,执行所有的onResolvedCallBacks
;reject
被调用时,执行所有的onRejectedCallBacks
;
js
class Promise1 {
constructor(executor) {
// 省略....
const resolve = (value) => {
// pending状态时,状态才可变更
if (this.state === PENDING) { // 2.1.2.1 must not transition to any other state.
this.state = FULFILLED;
this.value = value; // 2.1.2.2 must have a value, which must not change.
this.onResolvedCallBacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
// pending状态时,状态才可变更
if (this.state === PENDING) { // 2.1.3.1 must not transition to any other state.
this.state = REJECTED;
this.reason = reason; // 2.1.3.2 must have a reason, which must not change.
this.onRejectedCallBacks.forEach((fn) => fn());
}
};
// 省略....
}
then(onFulfilled, onRejected) {
// 2.2 A promise must provide a then method to access its current or eventual value or reason.
// A promise's then method accepts two arguments: onFulfilled AND onRejected
if (this.state === FULFILLED) {
// 省略....
} else if (this.state === REJECTED) {
// 省略....
} else if (this.state === PENDING) {
// 异步,状态为等待
// 观察者模式,观察state变化,缓存onFulfilled和onRejected
this.onResolvedCallBacks.push(() => {
onFulfilled(this.value);
});
this.onRejectedCallBacks.push(() => {
onRejected(this.reason);
});
}
}
}
- setTimeout模拟异步,再次测试
js
const promise = new Promise1((resolve, reject) => {
setTimeout(() => {
resolve('ok');
}, 1000);
}).then(data => {
console.log(data); // ok
});
const promise = new Promise1((resolve, reject) => {
setTimeout(() => {
reject('fail');
}, 1000);
}).then(data => {}, error => {
console.log(error); // fail
});
第三步
最最最最重要的核心逻辑来了,"众所周知"它又来了,众所周知,promise
是允许链式调用
的。
js
const promise = new Promise1((resolve, reject) => {
setTimeout(() => {
resolve('ok');
}, 1000);
}).then(data => {
return data;
}).then(data2 => {
console.log(data2); // ok
});
分析
文档2.2.7
说到then must return a promise
, then 必须返回一个promise,也正因为返回一个promise,我们才能够不断的调用其then
方法。
那么疑问来了,完成第一、二步后,我们返回的都是一个静态的值value
或者reason
,需要返回一个promise
,能不能直接返回this呢?
答案很显然是否定的,因为promise
的状态一旦改变之后是不可逆的,如果返回当前的this
状态不可逆将没有任何意义,所以我们需要返回一个new Promise。
js
class Promise1 {
constructor(executor) {
// 省略....
}
then(onFulfilled, onRejected) {
if (this.state === FULFILLED) {
// 省略....
} else if (this.state === REJECTED) {
// 省略....
} else if (this.state === PENDING) {
// 省略....
}
const promise2 = new Promise1((resolve, reject) => {
});
return promise2;
}
}
把测试案例贴在这里对比,会比较明显看出问题。
js
const promise = new Promise1((resolve, reject) => {
setTimeout(() => {
resolve('ok');
}, 1000);
}).then(data => {
return data; // a
});
promise.then(data2 => { // b
console.log(data2); // undefined
});
确实已经返回了一个新的promise
,并且可以调用then
方法,但在b步骤中的data2是undefined
,我们要如何将a步骤的数据传递给b呢?
显而易见,data
就是第一个promise
的onFulfilled
运行结果,官方文档2.2.7.1也有相关叙述
onFulfilled
和onRejected
的运行结果为x
,把then
的运行逻辑丢进promise2
里,将x
返回给promise2
即可。
js
class Promise1 {
constructor(executor) {
// 省略....
}
then(onFulfilled, onRejected) {
const promise2 = new Promise1((resolve, reject) => {
// 2.2 A promise must provide a then method to access its current or eventual value or reason.
// A promise's then method accepts two arguments: onFulfilled AND onRejected
if (this.state === FULFILLED) {
// 当前状态为成功态调用onFulfilled
const x = onFulfilled(this.value);
resolve(x);
} else if (this.state === REJECTED) {
// 当前状态为失败态调用onRejected
const x = onRejected(this.reason);
reject(x);
} else if (this.state === PENDING) {
// 异步,状态为等待
// 观察者模式,观察state变化,缓存onFulfilled和onRejected
this.onResolvedCallBacks.push(() => {
const x = onFulfilled(this.value);
resolve(x);
});
this.onRejectedCallBacks.push(() => {
const x = onRejected(this.reason);
reject(x);
});
}
});
return promise2;
}
}
继续阅读2.2.7.1后半句run the Promise Resolution Procedure [[Resolve]](promise2, x)
,运行一个promise的解析函数,意思就是要把promise2
的onFulfilled
和onRejected
的运行结果用一个处理函数进行处理,处理什么呢?当promise2
的onFulfilled
和onRejected
运行结果有可能还是一个promise,因此需要进行处理,并且把promise2
也以参数的形式传给处理函数。
再分析
promise2
以参数的形式传给处理函数,按照上述情况很明显是获取不到promise2
实例的,再看看官方文档3.1
scss
简而言之,通过"宏任务"机制或者"微任务"机制来确保这个异步执行,这里紧用`setTimeout`实现
```js
function resolvePromise(x, promise2, resolve, reject) {
}
class Promise1 {
constructor(executor) {
// 省略....
}
then(onFulfilled, onRejected) {
const promise2 = new Promise1((resolve, reject) => {
if (this.state === FULFILLED) {
// 成功态调用
// 获取fulfilled返回结果,根据返回结果判断是调用promise2的resolve还是reject
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(x, promise2, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
} else if (this.state === REJECTED) {
// 失败态调用
// 获取rejected返回结果,根据返回结果判断是调用promise2的resolve还是reject
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(x, promise2, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
} else if (this.state === PENDING) {
// 异步,状态为等待
// 观察者模式,观察state变化,缓存onFulfilled和onRejected
this.onResolvedCallBacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(x, promise2, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejectedCallBacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(x, promise2, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return promise2;
}
}
```
- 接下来就剩下
resolvePromise
的实现了,可以在then
方法中返回一个promise
,看看文档2.3 The Promise Resolution Procedure,根据文档的判断逻辑,一步一步进行判断即可。
js
function resolvePromise(x, promise2, resolve, reject) {
// 2.3.1 If promise and x refer to the same object, reject promise with a TypeError as the reason.
// 如果引用了自己,需要抛出一个错误。
if (x === promise2) {
return reject(new TypeError('循环引用promise'));
}
// 2.3.3.3 If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
let called = false;
if ((typeof x === 'object' && x !== null) || typeof x === 'function') { // 2.3.3 Otherwise, if x is an object or function
// 这种情况才有可能是promise
// Let then be x.then
try {
let then = x.then; // 2.3.3.1 Let then be x.then
if (typeof then === 'function') {
// 2.3.3.3 If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where
then.call(
x,
(y) => { // 2.3.3.3.1 If/when resolvePromise is called with a value y, run [[Resolve]](promise, y).
if (called) return;
called = true;
resolvePromise(y, promise2, resolve, reject);
},
(r) => { // 2.3.3.3.2 If/when rejectPromise is called with a reason r, reject promise with r.
if (called) return;
called = true;
reject(r);
}
);
} else {
// 2.3.3.4 If x is not an object or function, fulfill promise with x
// x是普通值, 直接返回
resolve(x);
}
} catch (error) {
// 2.3.3.2 If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
if (called) return;
called = true;
reject(error);
}
} else {
// 2.3.3.4 If x is not an object or function, fulfill promise with x
// x是普通值, 直接返回
resolve(x);
}
}
完整代码
js
const PENDING = 'PENDING'; // 默认态
const FULFILLED = 'FULFILLED'; // 成功态
const REJECTED = 'REJECTED'; // 失败态
function resolvePromise(x, promise2, resolve, reject) {
// 2.3.1 If promise and x refer to the same object, reject promise with a TypeError as the reason.
// 如果引用了自己,需要抛出一个错误。
if (x === promise2) {
return reject(new TypeError('循环引用promise'));
}
// 2.3.3.3 If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
let called = false;
if ((typeof x === 'object' && x !== null) || typeof x === 'function') { // 2.3.3 Otherwise, if x is an object or function
// 这种情况才有可能是promise
// Let then be x.then
try {
let then = x.then; // 2.3.3.1 Let then be x.then
if (typeof then === 'function') {
// 2.3.3.3 If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where
then.call(
x,
(y) => { // 2.3.3.3.1 If/when resolvePromise is called with a value y, run [[Resolve]](promise, y).
if (called) return;
called = true;
resolvePromise(y, promise2, resolve, reject);
},
(r) => { // 2.3.3.3.2 If/when rejectPromise is called with a reason r, reject promise with r.
if (called) return;
called = true;
reject(r);
}
);
} else {
// 2.3.3.4 If x is not an object or function, fulfill promise with x
// x是普通值, 直接返回
resolve(x);
}
} catch (error) {
// 2.3.3.2 If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
if (called) return;
called = true;
reject(error);
}
} else {
// 2.3.3.4 If x is not an object or function, fulfill promise with x
// x是普通值, 直接返回
resolve(x);
}
}
class Promise1 {
constructor(executor) {
this.state = PENDING; // 状态
this.reason = null; // 失败原因
this.value = null; // 成功数据
this.onResolvedCallBacks = []; // 成功态的回调缓存
this.onRejectedCallBacks = []; // 失败态的回调缓存
const resolve = (value) => {
// pending状态时,状态才可变更
if (this.state === PENDING) { // 2.1.2.1 must not transition to any other state.
this.state = FULFILLED;
this.value = value; // 2.1.2.2 must have a value, which must not change.
this.onResolvedCallBacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
// pending状态时,状态才可变更
if (this.state === PENDING) { // 2.1.3.1 must not transition to any other state.
this.state = REJECTED;
this.reason = reason; // 2.1.3.2 must have a reason, which must not change.
this.onRejectedCallBacks.forEach((fn) => fn());
}
};
// 能够改变promise状态的有resolve,reject和抛出异常throw new Error();
try {
executor(resolve, reject); // 默认会执行
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
// 值的穿透
// 2.2.1.1 If onFulfilled is not a function, it must be ignored.
// 2.2.1.2 If onRejected is not a function, it must be ignored.
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (v) => v;
onRejected =
typeof onRejected === 'function'
? onRejected
: (e) => {
throw e;
};
const promise2 = new Promise1((resolve, reject) => {
if (this.state === FULFILLED) {
// 成功态调用
// 获取fulfilled返回结果,根据返回结果判断是调用promise2的resolve还是reject
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(x, promise2, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
} else if (this.state === REJECTED) {
// 失败态调用
// 获取rejected返回结果,根据返回结果判断是调用promise2的resolve还是reject
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(x, promise2, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
} else if (this.state === PENDING) {
// 异步,状态为等待
// 观察者模式,观察state变化,缓存onFulfilled和onRejected
this.onResolvedCallBacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(x, promise2, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejectedCallBacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(x, promise2, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return promise2;
}
}