前言
基本每个学习前端的同学,都有一段必定要走的路,其中Promise 算是接触的比较早的一个异步解决方案,而且,不管是日常开发或者是面试中必定都经历过他,而平时也都是自己在使用,也没有深入了解原理,今天就自己实现一个Promise A+ 规范;
本文需要的知识点
- eventLoop(事件循环)
- 柯里化函数
- this指向
- 原型链
- ES6
铺垫
异步的解决方案
- callback回调函数 ----- 在node.js中体现尤为明显
- generator函数 ------- saga
- Promise ----------------- 主角
- async await ----- 日常使用
今天咱们主角是 Promise,咱们的Promise到底解决了什么问题呢?
- Promise的出现解决了 回调地狱 的问题,给大家贴张截图看下回调地狱
- 解决了回调地狱的同时,也解决了代码的可读性,可维护性问题;
Promise有什么特性吗?
首先 他有一个.then() 方法,其次 有三个状态 pending fulfilled rejected 三个状态,一旦进入某个状态后不可更改。而且还支持链式调用。
下面咱们看一下简单的异步代码:
// 异步代码的定义
var asyncFun = function(callback){
setTimeout(function(){
var randomMath = Math.random();
if(randomMath > 0.5){
callback(null,randomMath)
}else {
callback('数组小于0.5')
}
},1000)
}
asyncFun((err,res) => {
if(err){
console.log('err',err);
return;
}
console.log('res',res)
})
这段代码是在node中很常见,属于异步的回调函数,而回调函数的第一位是error,也算是一个默认的 '潜规则' 吧。
理想中Promise中就可以变成如下
new Prmise((resolve,reject)=>{
resolve('我要进行reslove')
}).then(res => {
console.log('res',res)
},err => {
console.log('err',err)
})
实现大体Promise框架
class MyPromise {
constructor(fn) {
// 在这里可以在做下判断不是function的判断
this.status = 'pending';
this.value = '';
this.reason = '';
this.resolve = this.resolve.bind(this); // 这个绑定this的代码可以抽成函数
this.reject = this.reject.bind(this);
fn(this.resolve, this.reject); // 在初始化的时候将 resolve reject函数放进去
}
resolve(value) {
if (this.status === MyPromise.PENDING) { // 当resolve时,只有状态符合 pending的时候
this.status = MyPromise.FULFILLED;
this.value = value; // 将值赋给res
console.log('value',value)
}
}
reject(value) {
if (this.status === MyPromise.PENDING) { // 这边同 resolve一样的判断
this.status = MyPromise.REJECTED;
this.reason = value;
}
}
}
MyPromise.PENDING = 'pending';
MyPromise.FULFILLED = 'fulfilled';
MyPromise.REJECTED = 'rejected';
let p = new MyPromise((resolve, reject) => {
resolve('我成功reslove了啊哈哈哈');
// reject('失败');
})
OK,到这,一个大致的Promise基本小框架就搭建完成了,主要的思想就是将函数当做参数传递,利用ES6的class类接收一个变量,这个变量的参数,在class里面提前定义好;
当然咱们一定要少用这些魔法字符串,可增加可维护性。
实现 基本(最简单) .then 回调功能;
class MyPromise {
constructor(fn) {
// 在这里可以在做下判断不是function的判断
this.status = 'pending';
this.value = '';
this.reason = '';
this.resolve = this.resolve.bind(this); // 这个绑定this的代码可以抽成函数
this.reject = this.reject.bind(this);
// 增加这个try catch 防止用户在resolve的直接报error
try{
fn(this.resolve, this.reject); // 在初始化的时候将 resolve reject函数放进去
}catch(e){
this.reject(e)
}
}
resolve(value) {
if (this.status === MyPromise.PENDING) { // 当resolve时,只有状态符合 pending的时候
this.status = MyPromise.FULFILLED;
this.value = value; // 将值赋给res
}
}
reject(value) {
if (this.status === MyPromise.PENDING) { // 这边同 resolve一样的判断
this.status = MyPromise.REJECTED;
this.reason = value;
}
}
// 最简单的then回调
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = function (value) {
return value
};
}
if (typeof onRejected !== 'function') {
onRejected = function (reason) {
throw reason;
}
}
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
onFulfilled(this.value)
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
onRejected(this.reason)
})
}
}
}
MyPromise.PENDING = 'pending';
MyPromise.FULFILLED = 'fulfilled';
MyPromise.REJECTED = 'rejected';
let p = new MyPromise((resolve, reject) => {
// throw new Error("看我newError位置")
resolve('我成功reslove了啊哈哈哈');
// reject('失败');
}).then((res) => {
console.log('res', res)
},err => {
console.log('err', err)
})
截止到目前这个同步的 .then 功能实现了,如果将 resolve 用setTimeout包裹住,这时候就会发现MyPromise 中的状态不对,一直是pending状态,而且打印不出来任何console;是因为eventLoop,setTimeout会进入异步队列,而这时我们的this.status的状态还是pending,所以我们定义数组来接收他;同时在resolve or reject中遍历,OK,按照这个思路咱们来写一下;
Promise异步处理
class MyPromise {
constructor(fn) {
// 在这里可以在做下判断不是function的判断
this.status = 'pending';
this.value = '';
this.reason = '';
this.resolve = this.resolve.bind(this); // 这个绑定this的代码可以抽成函数
this.reject = this.reject.bind(this);
this.onFulfilledCallback = [];
this.onRejectedCallback = [];
// 增加这个try catch 防止用户在resolve的直接报error
try {
fn(this.resolve, this.reject); // 在初始化的时候将 resolve reject函数放进去
} catch (e) {
this.reject(e)
}
}
resolve(value) {
if (this.status === MyPromise.PENDING) { // 当resolve时,只有状态符合 pending的时候
this.status = MyPromise.FULFILLED;
this.value = value; // 将值赋给res
this.onFulfilledCallback.forEach((fn) => fn(this.value));
}
}
reject(value) {
if (this.status === MyPromise.PENDING) { // 这边同 resolve一样的判断
this.status = MyPromise.REJECTED;
this.reason = value;
this.onRejectedCallback.forEach((fn) => fn(this.reason));
}
}
// 最简单的then回调
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = function (value) {
return value
};
}
if (typeof onRejected !== 'function') {
onRejected = function (reason) {
throw reason;
}
}
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
onFulfilled(this.value)
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
onRejected(this.reason)
})
}
if (this.status === MyPromise.PENDING) {
this.onFulfilledCallback.push(val => {
setTimeout(() => {
onFulfilled(val)
})
})
this.onRejectedCallback.push(reason => {
setTimeout(() => {
onRejected(reason)
})
})
}
}
}
MyPromise.PENDING = 'pending';
MyPromise.FULFILLED = 'fulfilled';
MyPromise.REJECTED = 'rejected';
let p = new MyPromise((resolve, reject) => {
// throw new Error("看我newError位置")
setTimeout(() => {
resolve('我成功reslove了啊哈哈哈');
})
// reject('失败');
}).then((res) => {
console.log('res', res)
}, err => {
console.log('err', err)
})
咱们这个想法正好可以将 异步 这个问题解决掉,异步解决掉了,众所周知,promise最大的特点是链式调用,而且一旦进入某个状态就不可更改了,就得改变then 后面得值,这样的话必须通过新的实例,而且每次 then回调需要防止报错,所以每个都需要加上try catch防止报错,咱们来试试怎么实现;
实现Promise 简单链式调用
class MyPromise {
constructor(fn) {
// 在这里可以在做下判断不是function的判断
this.status = MyPromise.PENDING;
this.value = '';
this.reason = '';
this.resolve = this.resolve.bind(this); // 这个绑定this的代码可以抽成函数
this.reject = this.reject.bind(this);
this.onFulfilledCallback = [];
this.onRejectedCallback = [];
// 增加这个try catch 防止用户在resolve的直接报error
try {
fn(this.resolve, this.reject); // 在初始化的时候将 resolve reject函数放进去
} catch (e) {
this.reject(e)
}
}
resolve(value) {
if (this.status === MyPromise.PENDING) { // 当resolve时,只有状态符合 pending的时候
this.status = MyPromise.FULFILLED;
this.value = value; // 将值赋给res
this.onFulfilledCallback.forEach((fn) => fn(this.value));
}
}
reject(value) {
if (this.status === MyPromise.PENDING) { // 这边同 resolve一样的判断
this.status = MyPromise.REJECTED;
this.reason = value;
this.onRejectedCallback.forEach((fn) => fn(this.reason));
}
}
// 最简单的then回调
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = function (value) {
return value
};
}
if (typeof onRejected !== 'function') {
onRejected = function (reason) {
throw reason;
}
}
// 这边是返回了一个新的promise对象,可以实现简单链式调用
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolve(x);
} catch (e) {
console.log('3',e)
reject(e);
}
})
}
// 加 setTimeout的作用防止resolve的时候直接跳过
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolve(x);
} catch (e) {
reject(e);
}
})
}
if (this.status === MyPromise.PENDING) {
this.onFulfilledCallback.push(val => {
setTimeout(() => {
try {
const x = onFulfilled(val);
resolve(x);
} catch (e) {
reject(e);
}
})
})
this.onRejectedCallback.push(reason => {
setTimeout(() => {
try {
const x = onRejected(reason);
resolve(x);
} catch (e) {
reject(e);
}
})
})
}
});
return promise2;
}
}
MyPromise.PENDING = 'pending';
MyPromise.FULFILLED = 'fulfilled';
MyPromise.REJECTED = 'rejected';
let p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('我成功reslove了啊哈哈哈');
})
}).then((res) => {
return '1111' + res
}, err => {
console.log('err', err)
}).then(res => {
console.log('000', res)
}, err => {
console.log('err222',err)
})
现在简单的链式调用已经完成了,还有一个问题是,如果 Promise.then 中用户直接return 出来了一个Promise 那么这样的话就不太行了,那这样的话咱们定义一个函数 resolvePromise ,专门处理这个逻辑;
实现Promise终极链式调用
class MyPromise {
constructor(fn) {
// 在这里可以在做下判断不是function的判断
this.status = MyPromise.PENDING;
this.value = '';
this.reason = '';
this.resolve = this.resolve.bind(this); // 这个绑定this的代码可以抽成函数
this.reject = this.reject.bind(this);
this.onFulfilledCallback = [];
this.onRejectedCallback = [];
// 增加这个try catch 防止用户在resolve的直接报error
try {
fn(this.resolve, this.reject); // 在初始化的时候将 resolve reject函数放进去
} catch (e) {
this.reject(e)
}
}
resolve(value) {
if (this.status === MyPromise.PENDING) { // 当resolve时,只有状态符合 pending的时候
this.status = MyPromise.FULFILLED;
this.value = value; // 将值赋给res
this.onFulfilledCallback.forEach((fn) => fn(this.value));
}
}
reject(value) {
if (this.status === MyPromise.PENDING) { // 这边同 resolve一样的判断
this.status = MyPromise.REJECTED;
this.reason = value;
this.onRejectedCallback.forEach((fn) => fn(this.reason));
}
}
// 最简单的then回调
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = function (value) {
return value
};
}
if (typeof onRejected !== 'function') {
onRejected = function (reason) {
throw reason;
}
}
// 这边是返回了一个新的promise对象,可以实现简单链式调用
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
MyPromise.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
console.log('3', e)
reject(e);
}
})
}
// 加 setTimeout的作用防止resolve的时候直接跳过
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason);
MyPromise.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
}
if (this.status === MyPromise.PENDING) {
this.onFulfilledCallback.push(val => {
setTimeout(() => {
try {
const x = onFulfilled(val);
resolve(x);
} catch (e) {
reject(e);
}
})
})
this.onRejectedCallback.push(reason => {
setTimeout(() => {
try {
const x = onRejected(reason);
resolve(x);
} catch (e) {
reject(e);
}
})
})
}
});
return promise2;
}
}
MyPromise.PENDING = 'pending';
MyPromise.FULFILLED = 'fulfilled';
MyPromise.REJECTED = 'rejected';
MyPromise.resolvePromise = function (promise2, x, resolve, reject) {
// 如果promise2 和 x 相等
if (promise2 === x) {
reject(new TypeError('Chaining cycle detected for promise'));
}
// 为了防止多次调用,如果多次调用,采用第一次的值!
let isCalled = false;
if (x instanceof MyPromise) {
// 如果为promise 直接 回调
x.then(
// res => resolve(res),
res => MyPromise.resolvePromise(promise2, res, resolve, reject),
reason => reject(reason)
);
// 这个if有坑,大家一定得明白下 typeOf null 也等于 Object
} else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 这边照例try catch
try {
const then = x.then; // 这边测试的时候很奇怪,为什么要这么写.....有大佬明白的可以指出
if (typeof then === 'function') {
then.call(x,
res => {
if (isCalled) return;
isCalled = true;
MyPromise.resolvePromise(promise2, res, resolve, reject)
},
reason => reject(reason)
)
} else {
if (isCalled) return;
isCalled = true;
resolve(x)
}
} catch (e) {
if (isCalled) return;
isCalled = true;
reject(e)
}
} else {
// 等到他只是值得时候进行resolve
resolve(x);
}
}
let p = new MyPromise((resolve, reject) => {
resolve('我成功reslove了啊哈哈哈');
}).then((res) => {
return new MyPromise((resolve, reject) => {
resolve(11111)
})
}, err => {
console.log('err', err)
}).then(res => {
console.log('000', res)
}, err => {
console.log('err222', err)
})
总结
现在大致的符合Promise A+ 规范 就实现完成了,大家也可以通过一些插件来测试咱们写的Promise是否符合规范。本文实现的比较简陋,后续抽空会继续更新;感谢支持
欢迎大神指点斧正
本文借鉴文章:
图灵社区 www.ituring.com.cn/article/665…