一、跟着 Promises/A+ 手写 Promise
1. 实现 Promise 的基本功能
1.1 代码:
// promise01.js
// Promise 的基本用法(executor、then 方法)
/**
* new Promise((resolve,reject)=>{})
* ① Promise 传入一个 executor 立即执行函数;
* ② executor 接收两个参数:resolve 函数和 reject 函数;
* Promise 有三个状态:pending、fulfilled、rejected;
* ① 状态只能从 pending => fulfilled,pending => rejected;
* ② 状态一旦确认,就不会再改变;
* Promise 都有 then 方法
* ① 接收两个可选参数:成功的回调 onFulfilled、失败的回调 onRejected;
* ② then 可以在同一个 Promise 上多次调用;
*/
const Pending = 'pending'
const Fulfilled = 'fulfilled'
const Rejected = 'rejected'
class MyPromise {
constructor(executor) { // promise 传入一个 executor 立即执行函数
// 定义在实例上的属性和方法,实例独有
this.state = Pending // 状态
this.value = undefined // 任何合法的js值(undefined、thenable:定义then方法的对象或函数、promise)
this.reason = undefined // 一个表示 promise 被reject 的原因
const resolve = (value) => {
if (this.state === Pending) {
this.state = Fulfilled
this.value = value
}
}
const reject = (reason) => {
if (this.state === Pending) {
this.state = Rejected
this.reason = reason
}
}
try { // 执行器抛出错误
executor(resolve, reject)// executor 有两个入参:resolve 函数和 reject 函数
} catch (e) { // 捕获异常,直接reject
reject(e)
}
}
// 定义在原型 prototype 上的方法或属性,所有实例共享(继承同一个)
then(onFulfilled, onRejected) {
if (this.state === Fulfilled) {
onFulfilled(this.value)
}
if (this.state === Rejected) {
onRejected(this.reason)
}
}
}
module.exports = MyPromise
1.2 测试代码
// ① Promise 的基本用法(executor、then 方法)
const MyPromise = require('./promise01')
const p = new MyPromise((resolve, reject) => {
// resolve('success') // ①
reject('filed') // ②
// throw Error('executor 中抛出错误') // ③
})
p.then((value) => {
console.log('fulfilled1:', value) // 执行① fulfilled1: success
}, (reason) => {
console.log('rejected1:', reason)
// 执行② rejected1: filed
// 执行③ rejected1: Error: executor 中抛出错误
})
p.then((value) => {
console.log('fulfilled2:', value) // fulfilled2: success
}, (reason) => {
console.log('rejected2:', reason)
// 执行② rejected2: filed
// 执行③ rejected2: Error: executor 中抛出错误
})
2. 实现 Promise 的异步调用
2.1 代码
// promise02.js
/**
* Promise 实现异步调用
* new Promise((resolve,reject)=>{})
* ① Promise 传入一个 executor 立即执行函数;
* ② executor 接收两个参数:resolve 函数和 reject 函数;
* Promise 有三个状态:pending、fulfilled、rejected;
* ① 状态只能从 pending => fulfilled,pending => rejected;
* ② 状态一旦确认,就不会再改变;
* Promise 都有 then 方法
* ① 接收两个可选参数:成功的回调 onFulfilled、失败的回调 onRejected;
* ② then 可以在同一个 Promise 上多次调用;
* Promise 的异步调用(发布订阅模式)
* ① 在 then 方法中订阅(收集成功或失败的回调)
* ② 在 resolve 方法中发布成功的回调(依次执行成功地回调函数)
* ③ 在 reject 方法中发布失败的回调(依次执行失败的回调函数)
*/
const Pending = 'pending'
const Fulfilled = 'fulfilled'
const Rejected = 'rejected'
class MyPromise {
constructor(executor) { // promise 传入一个 executor 立即执行函数
// 定义在实例上的属性和方法,实例独有
this.state = Pending // 状态
this.value = undefined // 任何合法的js值(undefined、thenable:定义then方法的对象或函数、promise)
this.reason = undefined // 一个表示 promise 被reject 的原因
this.onFulfilledCallbacks = [] // 存成功回调的数组
this.onRejectedCallbacks = [] // 存失败回调的数组
const resolve = (value) => {
if (this.state === Pending) {
this.state = Fulfilled
this.value = value
// 异步的情况发布订阅模式
// 发布:执行成功的回调
this.onFulfilledCallbacks.forEach(fn => fn())
}
}
const reject = (reason) => {
if (this.state === Pending) {
this.state = Rejected
this.reason = reason
// 异步的情况发布订阅模式
// 发布:执行失败的回调
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try { // 执行器抛出错误
executor(resolve, reject)// executor 有两个入参:resolve 函数和 reject 函数
} catch (e) { // 捕获异常,直接reject
reject(e)
}
}
// 定义在原型 prototype 上的方法或属性,所有实例共享(继承同一个)
then(onFulfilled, onRejected) {
if (this.state === Fulfilled) {
onFulfilled(this.value)
}
if (this.state === Rejected) {
onRejected(this.reason)
}
// 异步的情况发布订阅模式
// 订阅:收集成功或失败的回调
if (this.state === Pending) {
this.onFulfilledCallbacks.push(() => {
onFulfilled(this.value)
})
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
}
}
}
module.exports = MyPromise
2.2 测试代码
// ② Promise 的异步调用
const MyPromise = require('./promise02')
const p = new MyPromise((resolve, reject) => {
// 非异步
// resolve('success')
// reject('filed')
// 异步
setTimeout(() => {
resolve('success')
// reject('filed')
}, 2000)
})
p.then((value) => {
console.log('fulfilled1:', value)
}, (reason) => {
console.log('rejected1:', reason)
})
p.then((value) => {
console.log('fulfilled2:', value)
}, (reason) => {
console.log('rejected2:', reason)
})
// resolve('success')
// 2秒后打印:
// fulfilled1: success
// fulfilled2: success
// reject('filed')
// 2秒后打印:
// rejected1: filed
// rejected2: filed
3. 实现 Promise 的链式调用
3.1 代码
// promise.js
/**
* new Promise((resolve,reject)=>{})
* ① Promise 传入一个 executor 立即执行函数;
* ② executor 接收两个参数:resolve 函数和 reject 函数;
*
* Promise 有三个状态:pending、fulfilled、rejected;( Promises/A+ 2.1)
* ① 状态只能从 pending => fulfilled,pending => rejected;( Promises/A+ 2.1.1)
* ② 状态一旦确认,就不会再改变;(Promises/A+ 2.1.2,Promises/A+ 2.1.3)
*
* Promise 都有 then 方法(Promises/A+ 2.2);
* ① 接收两个可选函数形参:成功的回调 onFulfilled、失败的回调 onRejected;(Promises/A+ 2.2.1)
* ② onFulfilled 和 onRejected 都是 function;( Promises/A+ 2.2.2, Promises/A+ 2.2.3)
* ③ onFulfilled 和 onRejected 要等到同步代码执行完后再执行;( Promises/A+ 2.2.4)
* ④ then 可以在同一个 Promise 上多次调用;( Promises/A+ 2.2.6)
* Promise 的异步调用(发布订阅模式)
* ① 在 then 方法中订阅(收集成功或失败的回调)
* ② 在 resolve 方法中发布成功的回调(依次执行成功地回调函数)
* ③ 在 reject 方法中发布失败的回调(依次执行失败的回调函数)
*
* Promise 的链式调用
* ① then 必须返回一个 Promise:promise2 = promise1.then(onFulfilled,onRejected);( Promises/A+ 2.2.7)
* ①-① 定义 resolvePromise(promise2,x) 方法处理成功回调(onFulfilled)或失败回调(onRejected)返回的值 x;( Promises/A+ 2.2.7.1)
* ①-② 成功回调(onFulfilled)或失败回调(onRejected)报错,抛出错误的原因 reason;( Promises/A+ 2.2.7.2)
* ①-③ onFulfilled 不是 function ,直接返回 value 值;( Promises/A+ 2.2.7.3 )
* ①-④ onRejected 不是 function ,抛出 reason;( Promises/A+ 2.2.7.4 )
*
* ② resolvePromise(promise2,x);( Promises/A+ 2.3)
* ②-① promise2 和 x 是相同的引用,抛出 TypeError;( Promises/A+ 2.3.1)
* ②-② x 是 promise ,延用 x 的状态;( Promises/A+ 2.3.2)
* ②-③ x 是 object 或 function ;( Promises/A+ 2.3.3)
* ②-④ x 不是 object 或 function:resolve(x);( Promises/A+ 2.3.4)
*/
function resolvePromise(promise2, x, resolve, reject) {
/* promise2 和 x 是相同的引用,抛出 TypeError;( Promises/A+ 2.3.1)*/
if (promise2 === x) {
return reject(new TypeError('死循环了'))
}
/* x 是 object(非null)或 function;( Promises/A+ 2.3.3 )*/
if ((x && typeof x === 'object') || typeof x === 'function') {
// 只能调用一次(PromiseA+ 2.3.3.3.3);
let called = false
try { // PromiseA+ 2.3.3.2
/* PromiseA+ 2.3.3.1 */
const then = x.then
/* PromiseA+ 2.3.3.3;
* then 是 function;
* 调用 then ,x 作 this 传入,两个形参:resolvePromise、rejectPromise;
*/
if (typeof then === 'function') {
then.call(x, (y) => { // PromiseA+ 2.3.3.3.1
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, (r) => { // PromiseA+ 2.3.3.3.2
if (called) return
called = true
reject(r)
})
} else { // PromiseA+ 2.3.3.4
if (called) return
called = true
resolve(x)
}
} catch (e) { // PromiseA+ 2.3.3.2
if (called) return
called = true
reject(e)
}
} else { // x 不是 object 或 function ,返回 x 的值;( Promises/A+ 2.3.4 )
resolve(x)
}
}
/* Promise 有三种状态:pending、fulfilled、rejected;( Promises/A+ 2.1)*/
const Pending = 'pending'
const Fulfilled = 'fulfilled'
const Rejected = 'rejected'
class MyPromise {
constructor(executor) { // promise 传入一个 executor 立即执行函数
// 定义在实例上的属性和方法,实例独有
this.state = Pending // 状态
this.value = undefined // 任何合法的js值(undefined、thenable:定义then方法的对象或函数、promise)
this.reason = undefined // 一个表示 promise 被reject 的原因
// then 被调用多次,存在多个成功或失败的回调函数( Promises/A+ 2.2.6)
this.onFulfilledCallbacks = [] // 存成功回调的数组
this.onRejectedCallbacks = [] // 存失败回调的数组
const resolve = (value) => {
if (this.state === Pending) {
this.state = Fulfilled
this.value = value
// 异步的情况发布订阅模式
// 发布:执行成功的回调
this.onFulfilledCallbacks.forEach(fn => fn()) // ( Promises/A+ 2.2.6.1)
}
}
const reject = (reason) => {
if (this.state === Pending) {
this.state = Rejected
this.reason = reason
// 异步的情况发布订阅模式
// 发布:执行失败的回调
this.onRejectedCallbacks.forEach(fn => fn()) // ( Promises/A+ 2.2.6.2)
}
}
try { // 执行器抛出错误
executor(resolve, reject)// executor 有两个入参:resolve 函数和 reject 函数
} catch (e) { // 捕获异常,直接reject
reject(e)
}
}
// 定义在原型 prototype 上的方法或属性,所有实例共享(继承同一个)
/**
* then 方法
* 可选的两个形参;( Promises/A+ 2.2.1 )
* 形参作为 function 被调用;( Promises/A+ 2.2.5 )
*/
then(onFulfilled, onRejected) {
/**
* onFulfilled 不是 function ,直接返回 value 值;( Promises/A+ 2.2.7.3 )
* onRejected 不是 function ,抛出 reason;( Promises/A+ 2.2.7.4 )
*/
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
// 定义 promise2 接收 then 方法返回的 promise;
const promise2 = new MyPromise((resolve, reject) => {
if (this.state === Fulfilled) {
setTimeout(() => { // 模拟同步代码执行后再执行; Promises/A+ 2.2.4)
try { // onFulfilled 报错,抛出异常原因;( Promises/A+ 2.2.7.2)
const x = onFulfilled(this.value) // onFulfilled 返回 x 值;( Promises/A+ 2.2.7.1)
resolvePromise(promise2, x, resolve, reject) // 处理 x 的值;( Promises/A+ 2.3)
} catch (e) {
reject(e)
}
}, 0)
}
if (this.state === Rejected) {
setTimeout(() => { // 模拟同步代码执行后再执行;( Promises/A+ 2.2.4)
try { // onRejected 报错,抛出异常原因;( Promises/A+ 2.2.7.2)
const x = onRejected(this.reason) // onRejected 返回 x 值;( Promises/A+ 2.2.7.1)
resolvePromise(promise2, x, resolve, reject) // 处理 x 的值;( Promises/A+ 2.3)
} catch (e) {
reject(e)
}
}, 0)
}
// 异步的情况发布订阅模式
// 订阅:收集成功或失败的回调
if (this.state === Pending) {
this.onFulfilledCallbacks.push(() => {
try {
const x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
this.onRejectedCallbacks.push(() => {
try {
const x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
})
return promise2 // then 方法返回一个 promise( Promises/A+ 2.2.7)
}
}
module.exports = MyPromise
3.2 测试代码
const MyPromise = require('./promise')
const p1 = new MyPromise((resolve, reject) => {
resolve('p1')
})
const p2 = p1.then(() => {
// return new Error('error') // resolve: Error: error
// return Promise.resolve('promise.resolve') // resolve: promise.resolve
return new MyPromise((resolve, reject) => {
// resolve('p2') // resolve: p2
setTimeout(() => {
// resolve('MyPromise') // 2秒后 resolve: MyPromise
resolve(new MyPromise((resolve, reject) => {
resolve('多层嵌套返回 resolve') // 2 秒后 resolve: 多层嵌套返回 resolve
}))
}, 2000)
})
})
p2.then((value) => {
console.log('resolve:', value)
}, (value) => {
console.log('reject:', value)
})
4. 实现 Promise 其他方法
// catch、finally、resolve、reject、all
class MyPromise {
constructor(executor) { }
then(onFulfilled, onRejected) {}
catch(errorCallBack) {
return this.then(null, errorCallBack)
}
finally(callBack) {
return this.then(callBack, callBack)
}
// x 是 promise ,直接返回;
// x 如果是 thenable,跟随 thenable 对象的状态;
// 否则,直接 resolve(x)
static resolve(x) {
// x 是 promise ,返回 x;
if (x instanceof MyPromise) {
return x
}
return new MyPromise((resolve, reject) => {
if (x && x.then && typeof x.then === 'function') {
const then = x.then
setTimeout(() => {
then.call(x, resolve, reject)
}, 0)
} else {
resolve(x)
}
})
}
// 直接返回 reject 理由,变成后续方法的参数
static reject(r) {
return new MyPromise(reject => {
reject(r)
})
}
static all(iterable) {
return new MyPromise((resolve, reject) => {
if (Array.isArray(iterable)) {
const result = [] // 存储结果
let count = 0 // 计数器
// 长度为0 ,返回一个已经完成状态的 promise
if (iterable.length === 0) {
return resolve(iterable)
}
iterable.forEach((item, index) => {
if (item instanceof MyPromise) {
MyPromise.resolve(item).then(value => {
count++
result[index] = value
count === iterable.length && resolve(result)
}, reason => {
// 存在一个失败,直接返回失败的原因
reject(reason)
})
} else {
count++
result[index] = item // 不是 promise ,原样返回
count === iterable.length && resolve(result)
}
})
} else {
return reject(new TypeError('Argument is not iterable'))
}
})
}
}
// MyPromise.resolve = (x) => {}
// MyPromise.reject = (r) => {}
module.exports = MyPromise
5. 参考链接
二、原生 Promise 的一些'奇怪'现象
1. 例子一
1.1 打印什么?
Promise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(4);
}).then(() =>{
console.log(5);
})
Promise.resolve().then(() => {
console.log(6);
}).then(() => {
console.log(7);
}).then(() => {
console.log(8);
}).then(() => {
console.log(9);
}).then(() =>{
console.log(10);
})
1.2 思路&结果
注意:Js引擎为了让 microtask 尽快的输出,做了一些优化,连续的多个then(3个)如果没有 reject 或者 resolve 会交替执行 then 而不至于让一个堵太久(V8源码有体现) 。
① 微任务队列【1, 2, 3, 4, 5】
② 微任务队列【6, 7, 8, 9, 10】
①②交替执行;
结果:1、6、2、7、3、8、4、9、5、10
2. 例子二
2.1 打印什么?
Promise.resolve().then(() => {
console.log(1);
return Promise.resolve(2);
}).then((res) => {
console.log(res)
})
Promise.resolve().then(() => {
console.log(3);
}).then(() => {
console.log(4);
}).then(() => {
console.log(5);
}).then(() => {
console.log(6);
}).then(() =>{
console.log(7);
})
2.2 思路&结果
注意:在 chrome 内部实现的 Promise 和标准的 Promise/A+ 规范存在差异。
浏览器内部实现的区别:我们可以理解为,resolve 或者 return 一个 Promise 对象:
- 当发现 Promise.resolve() 时,会创建一个微任务(核心原理相当于调用 then 来处理一些逻辑);
- 当处理 Promise.resolve() 时,会创建一个微任务(参考手写的 resolvePromise(promise2,x) 方法,当处理 x 是 Promise 时,调用了一次 then 方法 );
Promise.resolve().then(() => {
console.log(1);
return Promise.resolve(2);
}).then((res) => {
console.log(res)
})
// 上面的代码可以理解为:
Promise.resolve().then(() => {
console.log(1);
return 2
})
.then()
.then()
.then((res) => {
console.log(res);
})
同理
new Promise(resolve=>{
resolve(Promise.resolve(1))
}).then((res) => { console.log(res) })
// 可以理解为:
new Promise(resolve => {
resolve(1);
})
.then()
.then()
.then((res) => { console.log(res) })
结合例子一和例子二:
① 微任务队列【1, , , 2】
② 微任务队列【3, 4, 5, 6, 7】
①②交替执行;
结果:1、3、4、5、2、6、7
3. 例子三
3.1 打印什么?
new Promise((resolve, reject) => {
console.log(1);
resolve();
}).then(() => {
console.log(2);
new Promise((resolve, reject) => {
console.log(3);
resolve();
}).then(() => {
console.log(4);
}).then(() => {
console.log(5);
});
return new Promise((resolve, reject) => {
console.log(6);
resolve();
}).then(() => {
console.log(7);
}).then(() => {
console.log(8);
});
}).then(() => { console.log(9); });
3.2 思路&结果
new Promise((resolve, reject) => {
console.log(1);
resolve();
}).then(() => {
console.log(2);
new Promise((resolve, reject) => {
console.log(3);
resolve();
}).then(() => {
console.log(4);
}).then(() => {
console.log(5);
});
// 下一个 then 必须等 return 的值
return new Promise((resolve, reject) => {
console.log(6);
resolve();
}).then(() => {
console.log(7);
}).then(() => {
console.log(8);
});
}).then(() => { console.log(9); });
① 处理宏任务,立即打印1,待处理的微任务队列【then、9】;
② 处理微任务,立即打印2、3、6,待处理的微任务队列【4、5】【7、8】、【9】;
③ 微任务【4、5】【7、8】交替打印4、7、5、8,9 必须等 8 执行完;
结果:1、2、3、6、4、7、5、8、9
4. 例子四
4.1 打印什么?
//第一段代码
new Promise((resolve, reject) => {
console.log(1);
resolve();
}).then(() => {
console.log(2);
return new Promise((resolve, reject) => {
console.log(3);
resolve();
}).then(() => {
console.log(4);
}).then(() => {
console.log(5);
});
}).then(() => { console.log(6); });
// 第二段代码
new Promise((resolve, reject) => {
console.log(11);
resolve();
}).then(() => {
console.log(22);
new Promise((resolve, reject) => {
console.log(33);
resolve();
}).then(() => {
console.log(44);
}).then(() => {
console.log(55);
});
}).then(() => { console.log(66); });
// 第三段代码
new Promise((resolve, reject) => {
console.log(111);
resolve();
}).then(() => {
console.log(222);
let p = new Promise((resolve, reject) => {
console.log(333);
resolve();
})
p.then(() => { console.log(444); })
p.then(() => { console.log(555); });
}).then(() => { console.log(666); });
4.2 思路&结果
//第一段代码
new Promise((resolve, reject) => {
console.log(1);
resolve();
}).then(() => {
console.log(2);
// return 一个 Promise ,下一个 then 必须等 Promise 的结果
return new Promise((resolve, reject) => {
console.log(3);
resolve();
}).then(() => {
console.log(4);
}).then(() => {
console.log(5);
});
}).then(() => { console.log(6); });
// 第一段代码结果:1、2、3、4、5、6
// 第二段代码
new Promise((resolve, reject) => {
console.log(11);
resolve();
}).then(() => {
console.log(22);
// 少了一个 return ,下一个 then 不用等待
new Promise((resolve, reject) => {
console.log(33);
resolve();
}).then(() => {
console.log(44);
}).then(() => {
console.log(55);
});
}).then(() => { console.log(66); });
// 第二段代码的结果:11、22、33、44、66、55
// 第三段代码
new Promise((resolve, reject) => {
console.log(111);
resolve();
}).then(() => {
console.log(222);
let p = new Promise((resolve, reject) => {
console.log(333);
resolve();
})
// then 非链式调用,不用等待
p.then(() => { console.log(444); })
p.then(() => { console.log(555); });
}).then(() => { console.log(666); });
// 第三段代码结果:111、222、333、444、555、666
三段代码一起考虑:
① 处理宏任务:立即打印1、11、111;待处理的微任务队列【then、6】、【then、66】、【then、666】
② 依次处理then:立即打印2、3、22、33、222、333;待处理的微任务队列【【4、5】、6】、【44、55】、【66】、【444】、【555】、【666】
③ 待处理队列交替执行:4、44、66、444、555、666、5、55、6
结果:1、11、111、2、3、22、33、222、333、4、44、66、444、555、666、5、55、6