从0手写自己的Promise
附带源码TS版本:https://github.com/Benzic/Promise/blob/master/Promise.ts
附带源码JS版本:https://github.com/Benzic/Promise/blob/master/Promise.js
帮忙点个star吧 谢谢
这是最后的TypeScript Type先声明在这里
interface PromiseType {
$$status: string, //状态
failCallBacks: PromiseType[], //成功存放的数组
successCallBacks: PromiseType[], //失败存放的数组
result: any, //成功的值
error: any, //失败的原因
then: (onFulfilled?: any, onRejected?: any) => PromiseType, //成功执行方法
catch: (fn?: Function) => PromiseType, //失败执行的方法
resolve: (params?: any) => void, //resolve方法
reject: (params?: any) => void, //reject方法
resolvePromise: (promise: PromiseType, func: PromiseType, resolve: Function, reject: Function) => void //链式调用
}
创建Promise构造函数
首先创建Promise构造函数,并且为 executor函数 传递参数。Promise一共有三个状态,Pending(等待)、Resolve(成功)、Reject(失败),初始Promise状态为pending。
export const Promise = function (executor: Function) {
let _this = this //'this' implicitly has type 'any' because it does not have a type annotation
_this.$$status = "pending"
executor(_this.resolve.bind(this), _this.reject.bind(this));
}
Promise.prototype.resolve = function () {
if (this.$$status === 'pending') {
this.$$status = 'success'
}
}
Promise.prototype.reject = function () {
if (this.$$status === 'pending') {
this.$$status = 'fail'
}
}
在ts下如果直接调用this 会报错,因为typeScript在 noImplicitThis 模式下,不允许this上下文隐式定义。解决方法有很多种,我这里选择的是直接添加this参数
export const Promise = function (this: PromiseType, executor: Function) {
let _this: PromiseType = this
_this.$$status = "pending"
executor(_this.resolve.bind(this), _this.reject.bind(this));
}
Promise.then
开始为Promise增加then方法,需要这几个对象存放信息,Promise成功的数组successCallBacks、失败数组failCallBacks。
export const Promise = function (this: PromiseType, executor: Function) {
let _this: PromiseType = this
_this.$$status = "pending"
_this.failCallBacks = []; //失败数组
_this.successCallBacks = []; //成功数组
executor(_this.resolve.bind(this), _this.reject.bind(this));
}
Promise.prototype.resolve = function (params: any) {
if (this.$$status === 'pending') {
this.$$status = 'success'
this.successCallBacks[0](params) //执行第一个成功函数
}
}
Promise.prototype.reject = function (params: any) {
if (this.$$status === 'pending') {
this.$$status = 'fail'
this.failCallBacks[0](params) //执行第一个失败函数
}
}
Promise.prototype.then = function (onFulfilled: Function, onRejected: Function) {
this.successCallBacks.push(onFulfilled) //把成功和失败的方法分别添加到对应数组当中
this.failCallBacks.push(onRejected)
}
Promise有一个叫做then的方法,里面有两个参数:onFulfilled,onRejected,成功有成功的值,失败有失败的原因。一个简单的拥有then方法的Promise方法就行了,但是在ts中调用却会报错,报错如下:
new Promise(function (resolve: Function, reject: Function) { //'new' expression, whose target lacks a construct signature, implicitly has an 'any' type
setTimeout(() => {
resolve("test promise")
}, 1000);
}).then((res: any) => {
console.log(res, 'then result')
})
解决方法也很简单只需要在Promise 函数后面 加上 as any即可 输出正常结果;
export const Promise = function (this: PromiseType, executor: Function) {
...
} as any
链式调用
Promise真正的精髓就是它的链式调用,所以不能链式调用的Promise函数那不叫Promise函数。
- 然而实际上不管是 then 还是 catch 方法调用,都返回了一个新的promise对象。为了达到链式,在then里面返回一个新的promise,称为promise2: promise2 = new Promise((resolve, reject)=>{})
•将这个promise2返回的值传递到下一个then中
•如果返回一个普通的值,则将普通的值传递给下一个then中 - 当我们在第一个then中 return 了一个参数(参数未知,需判断)。这个return出来的新的promise就是onFulfilled()或onRejected()的值,这里就需要一个resolvePromise来处理。
Promise.prototype.resolvePromise = function (promise: PromiseType, func: PromiseType, resolve: Function, reject: Function) {
let _this = this
if (func === promise) { //避免循环引用
return reject("error")
}
let called: any //防止多次调用
if (func && (typeof func === 'object' || typeof func === 'function')) {
try {
let then = func.then;
if (typeof then === 'function') {
//失败方法和成功方法只执行一次
then.call(func, (func2: PromiseType) => {
if (called) return;
called = true;
//返回新的promise,继续处理
_this.resolvePromise(promise, func2, resolve, reject);
}, (error: any) => {
if (called) return;
called = true;
reject(error);
})
} else { //直接输出成功
resolve(func);
}
} catch (error) { //直接输出失败
if (called) return;
called = true;
reject(error)
}
} else {
resolve(func);
}
}
改造resolve、reject方法,循环执行then方法
Promise.prototype.resolve = function (params: any) {
if (this.$$status === "pending") {
this.$$status = "success"
this.result = params;
if (!this.successCallBacks.length) return;
this.successCallBacks.map((a: Function) => a())
}
}
Promise.prototype.reject = function (params: any) {
if (this.$$status === "pending") {
this.$$status = "fail"
this.error = params
if (!this.failCallBacks.length) return;
this.failCallBacks.map((a: Function) => a())
}
}
改造then方法和Promise构造函数,异步执行
export const Promise = function (this: PromiseType, executor: Function) {
...
setTimeout(() => {
try {
executor(_this.resolve.bind(this), _this.reject.bind(this));
} catch (error) {
_this.reject.bind(this)(error)
}
});
} as any
Promise.prototype.then = function (onFulfilled: Function, onRejected: Function) {
let _this = this;
let newPromise = new Promise((resolve: Function, reject: Function) => {
if (_this.$$status === "success") {
setTimeout(() => {
try {
let func = onFulfilled(_this.result);
_this.resolvePromise(newPromise, func, resolve, reject)
} catch (error) {
reject(error)
}
})
}
if (_this.$$status === 'fail') {
setTimeout(() => {
try {
let func = onRejected(_this.error);
_this.resolvePromise(newPromise, func, resolve, reject);
} catch (error) {
reject(error)
}
})
}
if (_this.$$status === 'pending') {
_this.successCallBacks.push(() => {
setTimeout(() => {
try {
let func = onFulfilled(_this.result);
_this.resolvePromise(newPromise, func, resolve, reject)
} catch (error) {
reject(error)
}
})
})
_this.failCallBacks.push(() => {
setTimeout(() => {
try {
let func = onRejected(_this.error);
_this.resolvePromise(newPromise, func, resolve, reject);
} catch (error) {
reject(error)
}
})
})
}
})
return newPromise
}
试试结果
new Promise(function (resolve: Function, reject: Function) {
setTimeout(() => {
resolve("test promise")
}, 1000);
}).then((res: any) => {
console.log(res, 'then result')
return new Promise((resolve: Function, reject: Function) => {
resolve("test promise2")
})
}).then((res: any) => {
console.log(res, 'then result')
}).then((res: any) => {
console.log(res, 'then result')
})
链式调用
Promise.catch
接下来轮到Promise中的错误处理了,Promise.catch方法就变得非常简单的了,毕竟前面铺垫那么多代码,只需要三行代码即可。
Promise.prototype.catch = function (fn: Function) {
return this.then(null, fn) //直接把该方法放进failCallBacks数组
}
试试结果
new Promise(function (resolve: Function, reject: Function) {
setTimeout(() => {
resolve("test promise")
}, 1000);
}).then((res: any) => {
console.log(res, 'then result')
return new Promise((resolve: Function, reject: Function) => {
resolve("test promise2")
})
}).then((res: any) => {
console.log(res, 'then result')
}).then((res: any) => {
console.log(res, 'then result')
throw Error("test error")
}).catch((error: any) => {
console.log(error, 'catch error')
})
Promise.catch
Promise.resolve
Promise.resolve和reject的方法就非常简单了
Promise.resolve = function (val: any) {
if (val instanceof Promise) {
return val
}
return new Promise((resolve: Function, reject: Function) => {
resolve(val)
})
}
Promise.reject
Promise.reject = function (val: any) {
if (val instanceof Promise) {
return val
}
return new Promise((resolve: Function, reject: Function) => {
reject(val)
})
}
Promise.race
Promise.race = function (promises: any) {
if (!(promises instanceof Array)) {
throw new TypeError("parameter must be array")
}
let flag: boolean; //避免执行多次
return new Promise((resolve: Function, reject: Function) => {
promises.forEach((item: any) => {
if (item instanceof Promise) {
item.then((res: any) => {
if (!flag) {
flag = true;
resolve(res)
}
}).catch((error: any) => {
if (!flag) {
flag = true;
reject(error)
}
})
} else {
if (!flag) {
flag = true;
resolve(item)
}
}
})
})
}
试试结果
const promise1 = new Promise((resolve: any, reject: any) => {
setTimeout(() => {
resolve('promise1')
}, 5000);
})
const promise2 = new Promise((resolve: any, reject: any) => {
setTimeout(() => {
resolve('promise2')
}, 10000);
})
const promise3 = Promise.resolve("promise3")
const promise4 = 'promise4'
Promise.race([promise1, promise2, promise3, promise4]).then((res: any) => {
console.log(res)
})
Promise.race
因为Promise4只是字符串所以最先执行输出
Promise.all
all就是在race的基础上改进一下即可,不着急返回结果,先把结果保存下来,等最后一个promise执行完毕后再统一输出,所以:
Promise.all = function (promises: PromiseType) {
if (!(promises instanceof Array)) {
throw new TypeError("parameter must be array")
}
let count = 0//用于计数,当等于len时就resolve
let len = promises.length
let result: any = []//用于存放结果
return new Promise((resolve: Function, reject: Function) => {
if (!promises.length) {
resolve(result)
} else {
promises.forEach((item: any) => {
if (item instanceof Promise) {
item.then((data: any) => {
result[count++] = data
if (count === len) {
resolve(result)
}
}).catch((error: any) => {
reject(error)
})
} else {
result[count++] = item
if (count === len) {
resolve(result)
}
}
});
}
})
}
试试结果
const promise1 = new Promise((resolve: any, reject: any) => {
setTimeout(() => {
resolve('promise1')
}, 5000);
})
const promise2 = new Promise((resolve: any, reject: any) => {
setTimeout(() => {
resolve('promise2')
}, 10000);
})
const promise3 = Promise.resolve("promise3")
const promise4 = 'promise4'
Promise.all([promise1, promise2, promise3, promise4]).then((res: any) => {
console.log(res)
})
Promise.all
按着执行顺序输出结果,大功告成
所以这就是从0编写Promise的一个过程,只是自己浅显的见解而已,同时也是看过大神们的解析才能够写出来的结果。写出来也是为了方便自己理解。