Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。 @阮老师
先来看看promise
的几种用法:
- Promise.prototype.then:then方法有两个参数,第一个参数是Promise成功时执行的callback,第二个是Promise失败时执行的callback,then方法返回的是一个新的Promise实例
new Promise((resolve, reject) => resolve()).then(() => {
console.log('success');
}, () => {
console.log('err')
})
复制代码
- Promise.prototype.catch:用于指定发生错误时的回调函数
如果没有指定reject函数,最后就会执行catch函数
new Promise((resolve, reject) => {
reject('err');
})
.then()
.catch(e => {
console.log(e); // => Error: err
})
复制代码
- Promise.resolve:定义在Promise类本身的方法,可以通过Promise.resolve()调用,相当于直接将状态改为成功态
Promise.resolve('hahaha').then(data => {
console.log(data); // => 'hahaha'
})
复制代码
- Promise.reject:定义在Promise类本身的方法,可以通过Promise.reject()调用,相当于直接将状态改为失败
Promise.reject('hahaha').then(data => {
console.log(data); // => 'hahaha'
})
复制代码
- Promise.prototype.all:将多个Promise执行的结果放到一个数组中返回
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise1');
}, 1500);
})
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise2');
}, 2000);
})
Promise.all([promise1, promise2]).then(data => {
console.log(data); // => ["promise1", "promise2"]
})
复制代码
- ...
下面我们自己来实现一个符合 PromiseA+规范 的Promise
我们先来简单的用一下promise
:
console.log(1)
new Promise(() => {
console.log(2)
});
console.log(3)
复制代码
因为我们都知道promise
是异步的,所以按理会依次输出 1,3,2
然后我们运行之后发现 它依次输出的是 1,2,3
当我们使用then
的时候:
console.log(1)
Promise.resolve().then(() => {
console.log(2);
})
console.log(3)
复制代码
再次运行,发现结果和我们之前想的是一样的,会依次输出 1,3,2
这是因为promise
的callback
是立即执行的,只有then
方法是异步的
A promise must be in one of three states: pending, fulfilled, or rejected.
- promise必须有三个状态 pending(等待态) fulfilled(成功态) rejected(失败态)
- 当 state 是
pending
的时候- 可以将当前状态改变成其他状态(fulfilled or rejected)
- 当 state 是
fulfilled
的时候- 不能转换成其他状态
- 必须有一个
value
(成功之后的值)
- 当 state 是
rejected
的时候- 不能转换成其他状态
- 必须有一个
reason
(失败的原因)
- 当 state 是
class Promise {
constructor(executor) {
// 每一个promise实例都有自己的三个状态,我们用一个变量来保存当前的状态
this.status = 'pending';
// 用来保存执行成功时的值
this.value;
// 用来保存执行失败时的原因
this.reason;
// 用来将当前状态改变为成功态的方法
let resolve = val => {
// 只有当前状态是`pending` 才可以改变状态和值
if (this.status === 'pending') {
this.value = val;
this.status = 'fulfilled';
}
}
// 用来将当前状态改变为失败态的方法
let reject = reason => {
// 只有当前状态是`pending` 才可以改变状态和值
if (this.status === 'pending') {
this.reason = reason;
this.status = 'rejected';
}
}
// 执行executor可能会直接抛出异常,我们用try catch包起来
try {
executor(resolve, reject);
} catch (e) {
// 如果抛出异常,我们直接将状态改为失败态
reject(e);
}
}
}
复制代码
then
方法
一个promise必须提供一个
then
方法去获取当前的或最终的value or reason
promise的
then
方法有两个参数,分别是成功时执行的方法onFulfilled
和失败时执行的方法onRejected
onFulfilled
和onRejected
都是可选的参数- 如果没传,我们需要设置一个默认方法
- 如果
onFulfilled
是一个function
- 它必须在当前
promise
的状态变成fulfilled
之后被调用,将promise的value
(成功之后的值)作为他的第一个参数 - 在状态是
fulfilled
之前不能调用它 - 只能被调用一次
- 它必须在当前
- 如果
onRejected
是一个function
- 它必须在当前
promise
的状态变成rejected
之后被调用,将promise的reason
(失败的原因)作为他的第一个参数 - 在状态是
rejected
之前不能调用它 - 只能被调用一次
- 它必须在当前
promise.then(onFulfilled, onRejected)
复制代码
class Promise {
constructor(executor) {...}
then(onFulfilled, onRejected) {
// 如果当前状态是成功态,我们就执行成功的回调,并将存起来的成功的值value传过去
if (this.status === 'fulfilled') {
onFulfilled(this.value);
}
// 如果当前状态是失败态,我们就执行失败的回调,并将存起来的失败原因reason传过去
if (this.status === 'rejected') {
onRejected(this.reason);
}
}
}
复制代码
好了,我们现在已经可以简单的测试一下了
new Promise((resolve, reject) => {
resolve('完美');
}).then(data => {
console.log(data); // => 完美
})
复制代码
完美,但是那么问题来了,如果我们的 resolve
或 reject
是异步的呢?
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('完美');
}, 1000);
}).then(data => {
console.log(data);
}, err => {
console.log(err);
})
复制代码
运行之后我们发现什么都没有输出,onFulfilled
和 onRejected
都没有打印东西,那说明他们都没有执行,let me think think...
这是因为如果我们的 resolve
或 reject
是异步的,当我们的then
执行的时候,状态还没有改变,还是pending
状态,所以当然什么都不会执行。我们可以先把 onFulfilled
和 onRejected
存起来,等状态改变的时候依次执行对应的callback。A: 这难道是?B: 没错,这就是订阅发布模式。下面我们来改写一下:
class Promise {
constructor(executor) {
this.status = 'pending';
this.value;
this.reason;
// 用来保存成功时执行的回调函数
this.onSuccessCallback = [];
// 用来保存失败时执行的回调函数
this.onErrorCallback = [];
let resolve = val => {
if (this.status === 'pending') {
this.value = val;
this.status = 'fulfilled';
// 状态改变时 依次执行成功的回调
this.onSuccessCallback.forEach(fn => fn());
}
}
let reject = reason => {
if (this.status === 'pending') {
this.reason = reason;
this.status = 'rejected';
// 状态改变时 依次执行失败的回调
this.onErrorCallback.forEach(fn => fn());
}
}
// 执行executor可能会直接抛出异常,我们用try catch包起来
try {
executor(resolve, reject);
} catch (e) {
// 如果抛出异常,我们直接将状态改为失败态
reject(e);
}
}
then(onFulfilled, onRejected) {
if (this.status === 'fulfilled') {
onFulfilled(this.value);
}
if (this.status === 'rejected') {
onRejected(this.reason);
}
if (this.status === 'pending') {
// 将成功回调和失败回调都存起来,等待状态改变,再依次执行对应的方法
this.onSuccessCallback.push(() => {
onFulfilled(this.value);
});
this.onErrorCallback.push(() => {
onRejected(this.reason);
});
}
}
}
复制代码
-
onFulfilled
和onRejected
都是异步调用(微任务) -
onFulfilled
和onRejected
必须作为一个函数被执行 -
then
方法必须返回一个promise
promise2 = promise1.then(onFulfilled, onRejected) 复制代码
- 如果
onFulFilled
oronRejected
返回了一个值x
,运行promise解析程序 - 如果
onFulfilled
oronRejected
抛出了一个异常e
,promise2
的状态必须是rejected
,并将e
作为onRejected
的参数 - 如果
onFulfilled
不是一个function
并且promise1
是fulfilled
,promise2
必须也是fulfilled
并且使用和promise1
相同的value
- 如果
onRejected
不是一个function
并且promise1
是rejected
,promise2
必须也是rejected
并且使用和promise1
相同的reason
我们用promise的时候经常
promise.then().then().then() ...
这种写法叫链式调用,那怎么样才能继续调用then
方法呢,规范规定then
方法必须返回一个promise实例,这样就可以实现链式调用了class Promise { contructor() {...} then(onFulfilled, onRejected) { // 如果onFulfilled和onFulfilled 不是一个函数,我们给一个默认值 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val; onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }; // 我们将所有东西都包到一个promise实例中,最后返回这个实例,这样就可以实现链式调用 let promise2; // `onFulfilled` 和 `onRejected`都是异步调用,我们先用一个定时器实现异步调用 promise2 = new Promise((resolve, reject) => { if (this.status === 'fulfilled') { setTimeout(() => { try { // 有一个返回值x,运行解析函数resolvePromise let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }, 0); } if (this.status === 'rejected') { setTimeout(() => { try { // 有一个返回值x,运行解析函数resolvePromise let x = onRejected(this.reason);; resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }, 0); } if (this.status === 'pending') { // 将成功回调和失败回调都存起来,等待状态改变,再依次执行对应的方法 this.onSuccessCallback.push(() => { setTimeout(() => { try { // 有一个返回值x,运行解析函数resolvePromise let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }, 0); }); this.onErrorCallback.push(() => { setTimeout(() => { try { // 有一个返回值x,运行解析函数resolvePromise let x = onRejected(this.reason);; resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }, 0); }); } }); return promise2; } } 复制代码
这里可能就会有疑惑了,如果有一个返回值
x
就运行promise解析程序resolvePromise
,这是什么鬼?我们先来看看规范:
promise的解析程序是将
promise2
和x
作为参数的函数 - 如果
-
如果
promise2
和x
是一个promise,那么抛出一个TypeError
-
如果
x
是一个object
或function
- 使用变量
then
存储x.then
- 如果
x.then
会抛出异常e
,调用promise的reject
,并将e
作为它的参数 - 如果
then
是一个function
,使用call
把它的this
指向x
,它的第一个参数是resolvePromise
,第二个参数是rejectPromise
:- 当
resolvePromise
被y
值调用的时候,继续执行解析程序 - 当
rejectPromise
执行的时候,调用promise的reject
并将将失败原因r
作为它的参数
- 当
- 如果
then
不是一个object
orfunction
,调用promise的resolve
并将x
作为它的参数
- 使用变量
-
如果
x
不是一个object
orfunction
,调用promise的resolve
并将x
作为它的参数
总结下来就两点:
-
如果
promise2
和x
相等,就抛出一个TypeError
,我们先来看一下let p = new Promise((resolve, reject) => { // 返回当前promise实例 return p; }); p.then(data => { console.log(data); }, err => { console.log(err); }); 复制代码
运行上面代码,我们会发现promise抛出了一个异常,他告诉我们
TypeError: Chaining cycle detected for promise
,这是因为p
的成功还是失败取决于自己,自己再等待自己的执行结果,所以他既不会成功也不会失败- 如果
onFulFilled
oronRejected
返回了一个值x
,运行promise解析程序resolvePromise
返回值
x
有可能是一个常量,对象,也有可能是一个promise,这个程序的作用就是如果x
是一个promise,那就将x
一直解析到常量位置let p = new Promise((resolve, reject) => { resolve(new Promise((resolve, reject) => { resolve(1111); })) }) 复制代码
let resolvePromise = (promise2, x, resolve, reject) => { // 如果promise2和x相等,就抛出一个类型错误 if (promise2 === x) { return reject(new TypeError('错了')); } // 只允许调用一次resolvePromise let called; // 如果x不是一个常量继续解析 if (x !== null && (typeof x === 'function' || typeof x === 'object')) { // 调用x.then的时候可能会报错,因为我们有可能和别人的Promise库混用 try { let then = x.then; // 如果then是一个函数,证明x是一个promise,继续解析 if (typeof then === 'function') { then.call(x, y => { if (called) { return; } else { called = true; } resolvePromise(promise2, y, resolve, reject); }, r => { if (called) { return; } else { called = true; } reject(r); }) } else { // 说明x可能是一个普通对象,不是一个promise resolve(x); } } catch (e) { if (called) { return; } else { called = true; } reject(e); } } else { // 说明x是一个常量,直接执行resolve resolve(x); } } 复制代码
接下来我们来实现
Promise.resolve
Promise.reject
Promise.all
class Promise { contructor {...} then() {...} // 其实Promise.reject和Promise.reject非常简单 static resolve(value) { return new Promise((resolve) => { resolve(value); }) } static reject(reason) { return new Promise((resolve, reject) => { reject(reason); }) } // all方法 static all(promises) { return new Promise((resolve, reject) => { // 用来保存结果 let arr = []; let index = 0; const saveData = (i, data) => { arr[i] = data; if (++index === promises.length) { resolve(arr); } } for (let i = 0; i < promises.length; i++) { promises[i].then(data => { saveData(i, data); }, reject) } }) } } 复制代码
好了,接下来我们来一个完整版的promise
class Promise { contructor() { this.status = 'pending'; this.value; this.reason; // 用来保存成功时执行的回调函数 this.onSuccessCallback = []; // 用来保存失败时执行的回调函数 this.onErrorCallback = []; let resolve = val => { if (this.status === 'pending') { this.value = val; this.status = 'fulfilled'; // 状态改变时 依次执行成功的回调 this.onSuccessCallback.forEach(fn => fn()); } } let reject = reason => { if (this.status === 'pending') { this.reason = reason; this.status = 'rejected'; // 状态改变时 依次执行失败的回调 this.onErrorCallback.forEach(fn => fn()); } } // 执行executor可能会直接抛出异常,我们用try catch包起来 try { executor(resolve, reject); } catch (e) { // 如果抛出异常,我们直接将状态改为失败态 reject(e); } } static resolve(value) { return new Promise((resolve) => { resolve(value); }) } static reject(reason) { return new Promise((resolve, reject) => { reject(reason); }) } static all(promises) { return new Promise((resolve, reject) => { // 用来保存结果 let arr = []; let index = 0; const saveData = (i, data) => { arr[i] = data; if (++index === promises.length) { resolve(arr); } } for (let i = 0; i < promises.length; i++) { promises[i].then(data => { saveData(i, data); }, reject) } }) } then(onFulfilled, onRejected) { // 如果onFulfilled和onFulfilled 不是一个函数,我们给一个默认值 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val; onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }; // 我们将所有东西都包到一个promise实例中,最后返回这个实例,这样就可以实现链式调用 let promise2; // `onFulfilled` 和 `onRejected`都是异步调用,我们先用一个定时器实现异步调用 promise2 = new Promise((resolve, reject) => { if (this.status === 'fulfilled') { setTimeout(() => { try { // 有一个返回值x,运行解析函数resolvePromise let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }, 0); } if (this.status === 'rejected') { setTimeout(() => { try { // 有一个返回值x,运行解析函数resolvePromise let x = onRejected(this.reason);; resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }, 0); } if (this.status === 'pending') { // 将成功回调和失败回调都存起来,等待状态改变,再依次执行对应的方法 this.onSuccessCallback.push(() => { setTimeout(() => { try { // 有一个返回值x,运行解析函数resolvePromise let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }, 0); }); this.onErrorCallback.push(() => { setTimeout(() => { try { // 有一个返回值x,运行解析函数resolvePromise let x = onRejected(this.reason);; resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }, 0); }); } }); return promise2; } } let resolvePromise = (promise2, x, resolve, reject) => { // 如果promise2和x相等,就抛出一个类型错误 if (promise2 === x) { return reject(new TypeError('错了')); } // 只允许调用一次resolvePromise let called; // 如果x不是一个常量继续解析 if (x !== null && (typeof x === 'function' || typeof x === 'object')) { // 调用x.then的时候可能会报错,因为我们有可能和别人的Promise库混用 try { let then = x.then; // 如果then是一个函数,证明x是一个promise,继续解析 if (typeof then === 'function') { then.call(x, y => { if (called) { return; } else { called = true; } resolvePromise(promise2, y, resolve, reject); }, r => { if (called) { return; } else { called = true; } reject(r); }) } else { // 说明x可能是一个普通对象,不是一个promise resolve(x); } } catch (e) { if (called) { return; } else { called = true; } reject(e); } } else { // 说明x是一个常量,直接执行resolve resolve(x); } } 复制代码
- 如果