前言
手写Promise可谓是JS码农们的一个必备技能,熟练手写之后,不但能进一步较好地熟悉JS诸多机制,而且还能锻炼对代码编写细节的操控能力。互联网上手写Promise的范例超级多,好多优秀选手对它可谓是熟练于心呀。不多说,今天我就借鉴各位江湖高手的手法,总结一下我个人对于Promise这门武林秘籍的看法。
源码:
(需要请自取,我这个版本肯定也有一些细节问题没有考虑到,比如一些安全检测)
class Promise {
static PENDING = 'PENDING';
static REJECTED = 'REJECTED';
static FULFILLED = 'FULFILLED';
constructor(callback) {
//如果不是回调函数
if (!(callback instanceof Function)) callback = () => {}
this.state = Promise.PENDING
this.result = null
this.onFulfilledcallbacks = []
this.onRejectedcallbacks = []
const resolve = (data) => {
if (this.state == Promise.PENDING) {
this.state = Promise.FULFILLED
this.result = data
}
if (this.onFulfilledcallbacks) {
setTimeout(() => {
this.onFulfilledcallbacks.forEach(callback => {
callback(data)
})
});
}
}
const reject = (data) => {
if (this.state == Promise.PENDING) {
this.state = Promise.REJECTED
this.result = data
if (this.onRejectedcallbacks) {
setTimeout(() => {
this.onRejectedcallbacks.forEach(callback => {
callback(data)
})
})
}
}
}
try {
callback(resolve, reject)
} catch (e) {
reject(e)
}
}
then(onResolve, onReject) {
return new Promise((resolve, reject) => {
const callback = (type) => {
try {
const result = type(this.result)
if (result instanceof Promise) {
//如果是 解构Promise对象
result.then((res) => {
resolve(res)
}, (err) => {
reject(err)
})
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
}
if (this.state == Promise.REJECTED) {
setTimeout(() => {
callback(onReject)
});
}
if (this.state == Promise.FULFILLED) {
setTimeout(() => {
callback(onResolve)
})
}
if (this.state == Promise.PENDING) {
this.onFulfilledcallbacks.push(() => {
callback(onResolve)
})
this.onRejectedcallbacks.push(() => {
callback(onReject)
})
}
})
}
static all(promises) {
return new Promise((resolve, reject) => {
const arr = []
let index = 0
for (let promise of promises) {
//如果元素是Promise类型
const currentIndex = index
if (promise instanceof Promise) {
promise.then((r) => {
arr[currentIndex] = r
if (Object.keys(arr).length == promises.length) resolve(arr)
}, (e) => {
reject(e)
})
} else {
arr[currentIndex] = promise
if (Object.keys(arr).length == promises.length) resolve(arr)
}
index++
}
})
}
static race(promises){
return new Promise((resolve,reject)=>{
for(let promise of promises){
if(promise instanceof Promise){
promise.then((r)=>{
resolve(r)
},(e)=>{
reject(e)
})
}
else {
resolve(promise)
}
}
})
}
static allsettled(promises){
return new Promise((resolve,reject)=>{
const arr=[]
let index=0
for(let promise of promises){
//保存当前块index的值
const currentIndex=index
if(promise instanceof Promise){
promise.then(()=>{
arr[currentIndex]={
status:promise.state,
value:promise.result,
}
if(Object.keys(arr).length==promises.length)resolve(arr)
},()=>{
arr[currentIndex]={
status:promise.state,
reason:promise.result,
}
if(Object.keys(arr).length==promises.length)resolve(arr)
})
}
else{
arr[currentIndex]={
status:Promise.FULFILLED,
value:promise,
}
if(Object.keys(arr).length==promises.length)resolve(arr)
}
index++
}
})
}
//任意的promise完成,则返回这个完成的promise的信息
static any(promises){
return new Promise((resolve,reject)=>{
for(let promise of promises){
if(promise instanceof Promise){
promise.then((r)=>{
resolve(r)
})
}else{
resolve(promise)
break
}
}
reject('AggregateError: All promises were rejected')
})
}
static resolve(data) {
return new Promise((resolve, reject) => {
if(data instanceof Promise){
data.then((r)=>{
resolve(r)
},(e)=>{
reject(e)
})
}else{
resolve(data)
}
})
}
static reject(data) {
return new Promise((resolve, reject) => {
reject(data)
})
}
}
怎么样,源码是不是非常多啊,但是不急,咱们看的时候可以先从构造函数看,然后再是resolve、reject、then等等。主要呢还是要弄懂每一个方法的基本作用,这样手写下来其实就不难了。
1.constructor
constructor是类的构造函数,我们创建(new)一个类的实例的时候,就会运行这个构造函数。想一想Promise的构造函数的参数是怎么样的呢?
new Promise((resolve,reject)=>{...})
是的,就是这样的形式,因此我们需要在构造函数上添加一个形参(这里叫callback),作为回调函数,表示未来我们会在构造函数中调用在这个位置传入的函数(通过new)。
constructor(callback) {
//如果不是回调函数
if (!(callback instanceof Function)) callback = () => {}
this.state = Promise.PENDING
this.result = null
this.onFulfilledcallbacks = []
this.onRejectedcallbacks = []
const resolve = (data) => {
if (this.state == Promise.PENDING) {
this.state = Promise.FULFILLED
this.result = data
}
if (this.onFulfilledcallbacks) {
setTimeout(() => {
this.onFulfilledcallbacks.forEach(callback => {
callback(data)
})
});
}
}
const reject = (data) => {
if (this.state == Promise.PENDING) {
this.state = Promise.REJECTED
this.result = data
if (this.onRejectedcallbacks) {
setTimeout(() => {
this.onRejectedcallbacks.forEach(callback => {
callback(data)
})
})
}
}
}
try {
callback(resolve, reject)
} catch (e) {
reject(e)
}
}
resolve、reject
然后还应该有resolve、reject这样的函数,用于将Promise对象的状态改变为“成功”或“失败”,并将它们的参数设置为Promise对象的result值。
这里补一下Promise的状态的概念,Promise有三种状态,分别是“阻塞”、“成功”和“失败”,如果我们创建Promise实例的时候没有调用resolve和reject改变状态,那么Promise对象的状态就是默认值“阻塞”,而当Promise对象变为“成功”或“失败”的时候,它的状态是不可逆的,也就是不能再变成其他状态,这里在resolve和reject中加一个判断就是了,只有“阻塞”状态才能正确的reject和resolve。附图如下:
Promise对象有自己的状态state和result,分别表示Promise的当前状态和结果,其中结果是执行resolve(或者reject)时的参数
this.state = Promise.PENDING
this.result = null
2.then
我们知道,Promise使用then实现了链式调用,形如promise.then(...).then(...)。 “.”运算符使得我们摆脱了回调地狱的境遇。
对于Promise的方法,只要返回Promise对象,我们都是使用的return new Promise(...)这样的形式,实现起来既简单又高效,而且对于这里的then方法,它也的确应该返回的新的Promise。
为什么呢?我们知道then方法接受两个回调函数作为参数,第一个参数是Promise对象状态成功时要调用的函数,我这里称onResolve,第二个自然是状态失败时调用的函数,我这里称onReject。但不管它们谁最后被调用,只要它们返回了成功的值(也即成功的Promise或者非Promise值),then也应该返回成功的Promise;而执行它们时如果返回了失败的Promise,then最后也会返回失败的Promise。
最终,它们返回的期约的result(或是非Promise值)也会作为then返回的Promise的result的值。这就决定了,它们必须返回新的Promise,这个新的Promise的状态应该由then中的回调的执行成功或失败来决定,并且,它的result值由执行回调的返回值决定。
(传送门-->什么是回调地狱,如何用Promise解决回调地狱,看完这篇你就明白了。 - 掘金 (juejin.cn)) then方法对于调用者Promise对象的三种状态,有不同的处理方式。比如在对象“阻塞”时,会将其放入到对象自身的数据解构中保存,这里是数组
this.onFulfilledcallbacks = []
this.onRejectedcallbacks = []
等到Promise对象状态改变时,会根据成功或失败分别调用对应数组里面保存的回调
then(onResolve, onReject) {
return new Promise((resolve, reject) => {
const callback = (type) => {
try {
const result = type(this.result)
if (result instanceof Promise) {
//如果是 解构Promise对象
result.then((res) => {
resolve(res)
}, (err) => {
reject(err)
})
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
}
if (this.state == Promise.REJECTED) {
setTimeout(() => {
callback(onReject)
});
}
if (this.state == Promise.FULFILLED) {
setTimeout(() => {
callback(onResolve)
})
}
if (this.state == Promise.PENDING) {
this.onFulfilledcallbacks.push(() => {
callback(onResolve)
})
this.onRejectedcallbacks.push(() => {
callback(onReject)
})
}
})
}
3.resolve(静态方法)
之后的方法,我都只会介绍一下它们的作用。
Promise.resolve会依据传入的参数返回一个成功或失败的Promise对象。
-
从参数中得到的是一个Promise对象
resolve会等待这个Promise对象改变状态,将它的result作为resolve()返回的Promise对象的result,并且如果它失败了,会返回reject -
从参数中得到的是一个非Promise值
resolve会将其封装成一个Promise对象(new Promise),将其值作为这个对象的result,状态为成功
4.reject(静态方法)
和resolve类似,但是不管参数是什么,它直接返回一个失败的Promise对象,并将参数直接当作对象的result。
5.all(静态方法)
参数是一个可迭代对象(具有Symbol.iterator属性),一般传入数组,all会遍历数组元素。过程中只要第一次出现一个Promise对象改变为失败状态,那么all最终返回一个失败的Promise对象,并且result的值是这个第一个失败的Promise的result。只有所有的元素是一个成功的期约(包括非Promise值)时,all才返回一个成功的Promise对象,并且,result是这些元素按下标顺序排列的,由Promise的result值和非Promise值组成的数组。
6.race(静态方法)
参数和all相同,唯一的区别是数组元素只要出现了一个成功(或是非Promise值)或失败的Promise对象,那么就会返回这个值“构成”的Promise对象
7.any(静态方法)
参数同上,只要出现一个成功的Promise(或是非Promise值),返回一个成功的Promise,它的result不用我说也应该知道是啥吧?
8.allsettled(静态方法)
参数同上,数组元素如果是Promise对象,只要改变状态,就会插入到返回的result的对应下标位置;如果是非Promise值,则会当作成功的Promise对象(加一层封装),最终所有的元素改变状态了,返回的Promise状态变为成功,result值变为这些元素result组成的数组。
result形如
[{status:'fulfiled',value:'OK'},{status:'rejected',reason:'ERR'}]
这样的形式