之前也看过好多promise 的文章并手写过promise,只是会用,知道promise 的原理,但是它的思想到底是什么呢,直到我看到了函数式编程中的函子,才找到了答案, Promise 就是对函数式编程函子的应用,之前异步解决方案说过,异步方案的目的就是让异步代码写起来像同步,所以promise实现同步链式调用,这不就是函子的思想么
1.函子
class Functor{
constructor(value) {
this._value = value
}
map(fn) {
return Functor.of(fn(this._value))
}
static of(value) {
return new Functor(value)
}
}
上面就是函子,核心思想就是
-
每个函子Functor都是一个新的对象
-
对象的原型链上有 map 函数
-
通过 map 中传递进去的函数fn去处理函子保存的数据,用得到的值去生成新的函子,从而实现链式调用的值传递
2. 实现异步链式调用
上面函子的是同步链式调用,如果想要异步链式调用我们就需要传入一个执行器 excutor 包含一个参数 resolve 函数,延迟执行异步回调,
1. 第一步、根绝函子的思想,我们首先的拿到传入的值
class MyPromise{
constructor(excutor){
this._value = null
let resolve = (value) => {
this._value = value
console.log(this._value);
}
excutor(resolve)
}
}
new MyPromise((resolve) => {
setTimeout(()=>{
resolve('resolve')
}, 1000)
})
如上图所示我们 拿到了 外部传入的 resolve的值,这里需要注意一下 执行器 excutor 是由外向内传值,resolve 是 promise 内部定义,外部调用,这样就可以修改内部的_value值
2.第二步、还是根据函子的思想,拿到外部传入的值之后,我们要通过map 传入的fn 去处理拿到的值,但是由于是 fn 有可能是异步的,所以我们可以找一个队列存储下来,并将fn返回结果交给下一个 函子的 resolve 解决
为了理解方便我们直接把map 写成then
class MyPromise{
constructor(excutor){
this._value = null
this.callback = []
let resolve = (value) => {
this._value = value
this.callback && this.callback.forEach( item => item())
}
excutor(resolve)
}
then(fn) {
return new MyPromise((resolve) => {
this.callback.push(() => {
let data = fn(this._value)
resolve(data)
})
})
}
}
var promise = new MyPromise( (resolve) => {
setTimeout( () => {
resolve(1)
}, 1000)
})
promise.then((res) => {
return res + 1
}).then( res => {
console.log(res)
return res + 1
})
通过then方法向下手机 resolve 的callback,在 resolve 时候执行收集到的callback
3. 为MyPromise 函子增加失败回调 和 状态
有时候在异步回调时候不能拿到结果,需要返回一个错误信息,所以需要增加一个错误回调来处理异常,写法同成功回调
我们知道 promise 的初始状态是 pending ,完成状态是 fulfilled, 失败状态是 rejected, 在成功或者失败的callback修改 状态
const PENDING = 'pending'
const FULFILED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise{
constructor(excutor){
this._value = null
this._reason = null
this._status = PENDING
this.resolveCallback = []
this.rejectCallback = []
let resolve = (value) => {
this._value = value
this._status = FULFILED
this.resolveCallback && this.resolveCallback.forEach( item => item())
}
let reject = (reason) => {
this._reason = reason
this._status = REJECTED
this.rejectCallback && this.rejectCallback.forEach( item => item())
}
excutor(resolve, reject)
}
then(resolveFn, rejectFn) {
return new MyPromise((resolve, reject) => {
this.resolveCallback.push(() => {
let data = resolveFn(this._value)
resolve(data)
})
this.rejectCallback.push(() => {
let data = rejectFn(this._reason)
reject(data)
})
})
}
}
4. 增加状态的判断,一级 then 中 返回一个MyPromise的处理方式
**看到有些文章写异步处理, 用的是 setTimout 这里其实是不合理的,因为 setTimeout是一个宏任务,**而promise是一个微任务,用宏任务实现一个微任务,本质上是不合理的
所以我们用queueMicrotask,它其实也是用promise实现的一种微任务,这里为了通过后面promise A+的检测,还是使用queueMicrotask
resolve = (value) => {
if( this._status == PENDING ) {
queueMicrotask(() => {
this._value = value
this._status = FULFILED
this.resolveCallback && this.resolveCallback.forEach( item => item())
}, 0)
}
}
reject = (reason) => {
if( this._status == REJECTED ) {
queueMicrotask(() => {
this._reason = reason
this._status = REJECTED
this.rejectCallback && this.rejectCallback.forEach( item => item())
})
}
}
这里加了 _status 的判断,只有在 pending状态下才能修改 MyPromise的状态, 然后then方法中添加了 状态的判断,只有pending 才手机回调,其他状态直接执行对应状态的函数
then(resolveFn, rejectFn) {
return new MyPromise((resolve, reject) => {
if( this._status == PENDING ) {
this.resolveCallback.push(() => {
let data = resolveFn(this._value)
resolve(data)
})
this.rejectCallback.push(() => {
let data = rejectFn(this._reason)
reject(data)
})
}else if( this._status == FULFILED ) {
let data = resolveFn(this._value)
if( data instanceof MyPromise ) {
data.then( resolve, reject)
}else{
resolve(data) }
}else if( this._status == REJECTED ) {
reject(this._reason)
}
})
}
这里遇到如果MyPromise 返回的是一个MyPromise 对象时候需要将 这个返回的MyPromise的值传递下去
if( data instanceof MyPromise ) {
data.then( resolve, reject)
}
这样一个基本的Promise 就写好了,当然还有一系列判断要写,这个可以以后再完善,这里主要是理解Promise的原理
3.添加静态方法和实例方法
静态方法主要有Promise.resolve, Promise.reject, Promise.all, Promise.race,下面让我们实现他们
1.Promise.resolve, 这个其实很简单了就是生产一个状态为 fulfiled的Promise对象
static resolve(parameter) {
if(parameter instanceof MyPromise) {
return parameter }
return new MyPromise(resolve=>{
resolve(parameter) })
}
2.Promise.reject
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
3.Promise.all, 传入一个promise 对象list 列表中每个promise都返回结果才返回结果
static all (promiseList) {
return new MyPromise((resolve, reject) => {
const result = [];
const length = promiseList.length;
let count = 0;
if (length === 0) {
return resolve(result);
}
promiseList.forEach((promise, index) => {
MyPromise.resolve(promise).then((value) => {
count++;
result[index] = value;
if (count === length) {
resolve(result);
}
}, (reason) => {
reject(reason);
});
});
});
}
4.Promise.race ,race 顾名思义就是赛跑,list 中哪个结果返回的快就取哪个
static race (promiseList) {
return new MyPromise((resolve, reject) => {
const length = promiseList.length;
if (length === 0) {
return resolve();
} else {
for (let i = 0; i < length; i++) {
MyPromise.resolve(promiseList[i]).then((value) => {
return resolve(value);
}, (reason) => {
return reject(reason);
});
}
}
});
}
实例方法主要有catch 和 finally
1.promise.catch 就是捕获错误回调
catch (onRejected) {
// 只需要进行错误处理
this.then(undefined, onRejected);
}
2.promise.finally 不管promise 最后状态是 成功还是失败都会执行这个回调
finally (fn) {
return this.then((value) => {
return MyPromise.resolve(fn()).then(() => {
return value;
});
}, (error) => {
return MyPromise.resolve(fn()).then(() => {
throw error
});
});
}
4.验证通过Promise A+ 规范测试
1.安装 promises-aplus-tests
npm install promises-aplus-tests --save-dev
2. 添加测试代码
MyPromise.deferred = function () {
var result = {};
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve;
result.reject = reject;
});
return result;
}
module.exports = MyPromise;
3. package.json 中添加运行的命令
"scripts": {
"test": "promises-aplus-tests '文件名称'"
}
4. 运行 npm run test
如果运行之后看到这个画面,恭喜你成功写了一个Promise