前言
哈哈,好久没写博客了,主要是因为这些日子刚入职了网易,一直在适应工作,还有学校里各种乱七八糟的琐事,所以一直没有来得及写。今天刚好做完了一个迭代,难得有空闲时间来写篇博客(其实是摸鱼~)。
之前我也写过一篇手撕promise,只不过那篇只有代码,没有解释,所以不太容易理解。这篇我准备先从理论说起,再配合代码进行介绍。
如果在看这篇文章之前还没有了解promise的基础知识,请访问JS的Promise对象
哈哈哈哈,就不说废话了,直接进入正题。
一、手撕Promise必须知道的一些概念
1. 高阶函数
高阶函数的概念:
(1)一个函数的参数是一个函数,我们可以成这个函数为高阶函数。
(2)一个函数返回一个函数,我们可以称这个函数为高阶函数。
(3)符合以上任意一点,我们就可以称这个函数为高阶函数。
2. 装饰器模式
概念(摘自菜鸟教程):
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
将高阶函数和装饰器模式结合使用,举个例子:
function say(who) { // 普通的函数
console.log("say", who);
}
// 对原函数进行扩展 但是不破坏原函数
// @装饰器对类来扩展
Function.prototype.before = function (beforeSay) {
// 接受到了回调方法
return (...args) => {
// newSay
beforeSay(...args);
this(...args);
};
};
let beforeSay = (args) => {
// 传入一个回调方法
console.log("say before", args);
};
let newSay = say.before(beforeSay);
newSay("我"); // 这里调用的应该是新的方法
这里使用装饰器模式给原来的say方法加了一个新的功能beforeSay
3. 柯里化函数
我之前写过一篇柯里化函数的博客,简单来说,柯里化函数就是利用了闭包的预存储的功能,它将一个多个参数的函数,转化成一个个参数传入的函数。
举个例子:
// 原本fn有四个参数,a,b,c,d
function fn(a,b,c,d){...}
// 通过柯里化,可以转化成 fn(a)(b)(c)(d)
4. 发布订阅模式
发布订阅模式是前端中一种常见的设计模式vue中大量使用的观察者模式便是基于发布订阅模式衍变而来的。
发布订阅模式有一个发布者、一个订阅者和一个事件池。发布者将消息发布到事件池中,订阅者可以从事件池中订阅消息,事件池有一个on方法和一个emit方法,通过on方法发布消息,通过emit方法执行消息事件。在发布订阅模式中,发布者和订阅者没有强耦合关系。
举个例子(文件在node环境中执行):
const fs = require("fs");
let events = {
arr = [],
on(){
this.arr.push(fn);
}
emit(){
this.arr.forEach(fn=>fn());
}
}
events.on(function(){
console.log('每次读取完毕后都执行')
})
events.on(function(){
if(Object.keys(person).length === 2){
console.log('读取完毕')
}
})
let person= {};
fs.readFile("./a.txt", "utf8", function (err, data) {
person.name = data;
events.emit();
});
fs.readFile("./b.txt", "utf8", function (err, data) {
person.age = data;
events.emit();
});
5. 观察者模式
观察者模式中有一个观察者和一个被观察者,被观察者有一个自身状态,当自身状态改变了,通知所有观察者执行update方法触发事件。在观察者模式中,观察者和被观察者是强耦合的。
举个例子:
class Subject { // 被观察者 (需要有一个自身的状态 ,状态变化了要通知所有的观察者)
constructor(name){
this.name = name
this.observers = []
this.state = '我开心的在玩'
}
attach(o){
this.observers.push(o);
}
setState(newState){
this.state = newState;
this.observers.forEach(o=>o.update(this));
}
}
class Observer{ // 观察者
constructor(name){
this.name = name;
}
update(s){
console.log(this.name+":" + s.name +'当前的状态是'+s.state)
}
}
let s = new Subject('小宝宝')
let o1 = new Observer('爸爸');
let o2 = new Observer('妈妈');
// 订阅模式
s.attach(o1)
s.attach(o2)
s.setState('有人咬我,不开心')
s.setState('老师表扬我了 开心了')
6. 手写promise基础版
有了上面的基础,我们进行手写promise就会容易些了
首先根据promise的定义:
- promise是一个类,在使用的时候需要new这个类
- 在newPromise的时候,需要传入一个executor执行器,默认会立即被调用,而且参数有两个:resolve和reject
- promise有三个状态,分别是:pendding 默认等待态 onfulfilled 成功态 onrejected 失败态
我们的promise默认就是pendding,当用户调用resolve时会变成成功态,调用reject的时候会变成失败态
成功可以传入成功的原因,失败可以传入失败的原因 - new Promise 会返回一个promise实例 这个实例上有一个then方法 , then方法中有两个参数一个是成功的回调,一个是失败的回调
- 走向失败有两种情况:reject() 、用户主动抛出异常
- 一个promise中可以then多次 (发布订阅模式)
- promise的状态是不能从成功变成失败,也不能从失败变成成功 只有pending的时候才能更改状态
我们来写一个低配版promise
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
class Promise {
constructor(exector) {
this.status = PENDING;
this.value = undefined; // 成功的原因
this.reason = undefined; // 失败的原因
this.onResolvedCallbacks = []; // 存放成功的回调
this.onRejectedCallbacks = []; // 存放失败的回调
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach((fn) => fn());
}
}; // 每次new 都生成两个方法 reoslve,reject
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
try {
exector(resolve, reject); // 传递给用户两个参数
} catch (e) {
reject(e);
}
}
then(onFulfilled, onRejected) {
if (this.status == FULFILLED) {
onFulfilled(this.value);
}
if (this.status == REJECTED) {
onRejected(this.reason);
}
if (this.status == PENDING) {
// 稍后成功了 除了执行回调外 还有其他的逻辑
this.onResolvedCallbacks.push(() => {
// todo...
onFulfilled(this.value);
});
this.onRejectedCallbacks.push(() => {
// todo...
onRejected(this.reason);
});
}
}
}
module.exports = Promise;
7. 手写promise完全版
接下来让我们完善一下我们写的promise,根据promiseA+规范:
- then方法必须返回一个promise
- 如果onFulfilled或onRejected返回一个x,就执行 Promise Resolution procedure 并把x传进去
- 如果onFulfilled不是一个函数并且我们的promise实例已经为满足态,则then返回的promise实例必须为满足态并且和我们的promise实例有相同的值
- 如果onRejected不是一个函数并且我们的promise实例已经为失败态,则then返回的promise实例必须为失败态并且和我们的promise实例有相同的失败原因
- 并且为了执行 Promise Resolution procedure ,我们要为其增加一个promise规范中最核心的一个方法:resolvePromise。
上代码:
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function resolvePromise(x, promise2, resolve, reject) {
// x 决定promise2的状态走成功还是失败
// 所有promise都要遵循这个规范,这样就可以保证不同人写的promise可以混用
// 核心就在这个resolvePromise方法中
}
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
}
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch (e) {
resolve(e);
}
}
then(onFulfilledCallback, onRejectedCallback) {
let promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(()=>{
try{
let x = onFulfilledCallback(this.value);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === REJECTED) {
setTimeout(()=>{
try{
let x = onRejectedCallback(this.reason);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === PENDING) {
this.onFulfilledCallbacks.push(() => {
setTimeout(()=>{
try {
let x = onFulfilledCallback(this.value);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(()=>{
try{
let x = onRejectedCallback(this.reason);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
return promise2;
}
});
}
}
module.exports = Promise;
上述内容完成后,我们自己的promise已经初具雏形了,接下来就让我们来写promise中最核心的一个方法:resolvePromise方法。同样是根据Promise/A+规范:
在这里插入图片描述
- 如果promise和x引用了同一个对象,则返回一个拒绝态的promise,结果为一个TypeError
- 如果x是一个promise
- 如果x状态为pending,promise必须保持pending状态知道x变成fulfilled态或者rejected态
- 当x被满足时(就是变成满足态时),以相同的值来执行promise
- 当x被拒绝时,以相同的原因来执行promise
- 除此之外,如果x是一个对象或者一个函数(这个地方用来判断x是否为一个promise,是用来规范不同的人写的promise的,并且这条规则可以覆盖上三条规则)
- 定义一个then变量,把x.then赋值给它
- 如果检索属性x.then导致抛出异常e,则以e为理由拒绝promise
- 如果then是一个函数,则以x为它的this调用它,第一个参数resolvePromise,第二个参数rejectPromise,其中:
- 如果/当resolvePromise值为y时,执行[[Resolve]](promise, y)
- 如果/当rejectPromise的原因是r,则用r拒绝promise
- 如果同时调用resolvePromise和rejectPromise,或者对同一参数进行多次调用,则第一个调用优先,其他调用将被忽略
- 如果调用then抛出异常e
- 如果resolvePromise或rejectPromise被调用,则忽略它
- 否则,以e为理由拒绝promise
- 如果then不是函数,让x变成满足态
- 如果x不是一个对象或函数,让x变成满足态
上代码:
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function resolvePromise(x, promise2, resolve, reject) {
// x 决定promise2的状态走成功还是失败
if (promise2 === x) {
return reject(new TypeError("循环引用"));
}
// 判断x是不是一个promise先保证x得是一个对象或者函数,如果不是对象或者函数,则x一定不是promise
if ((typeof x === "object" && x !== null) || typeof x === "function") {
let called; // 我们用called判断下面过程是否执行过了,如果执行过了,就不再执行
// 我们需要看这个x上有没有then方法,有then方法才说明它是一个promise
try {
let then = x.then; //x可能是别人写的promise,那么取then有风险
if (typeof then === "function") {
then.call(x, y => {
if (called) return;
called = true;
resolvePromise(y, promise2, resolve, reject); // 递归解析直到我们的y的值是一个普通值
}, r => {
if (called) return;
called = true;
reject(r);
})
} else { // 没有then方法都执行这里
resolve(x); // 这里x只是一个普通对象
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
// 这里x只是一个普通的值,直接把x传给promise2即可
resolve(x);
}
// 所有promise都要遵循这个规范,这样就可以保证不同人写的promise可以混用
// 核心就在这个resolvePromise方法中
}
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
}
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulfilledCallback, onRejectedCallback) {
// 有可能这个onFulfilledCallback,onRejectedCallback是可选的,所以用户没填,我们要自己给它补上(穿透特性)
onFulfilledCallback = typeof onFulfilledCallback === "function" ? onFulfilledCallback : function (data) {
return data;
};
onRejectedCallback = typeof onRejectedCallback === "function" ? onRejectedCallback : err =>{
throw err;
};
let promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilledCallback(this.value);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejectedCallback(this.reason);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === PENDING) {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilledCallback(this.value);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejectedCallback(this.reason);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
}
// 安装此模块来测试自己写的promise是否符合规范,并且要加上Promise.deferred
// npm install promises-aplus-tests -g
// promises-aplus-tests 3.promise
// catch Promise.resolve Promise.reject
// 在测试的时候 会测试你的promise对象是否符合规范
Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd
}
module.exports = Promise;
附上一个通过测试的截图
8. 完善promise其他功能
加入catch、all、finally方法
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function resolvePromise(x, promise2, resolve, reject) {
// x 决定promise2的状态走成功还是失败
if (promise2 === x) {
return reject(new TypeError("循环引用"));
}
// 判断x是不是一个promise先保证x得是一个对象或者函数,如果不是对象或者函数,则x一定不是promise
if ((typeof x === "object" && x !== null) || typeof x === "function") {
let called; // 我们用called判断下面过程是否执行过了,如果执行过了,就不再执行
// 我们需要看这个x上有没有then方法,有then方法才说明它是一个promise
try {
let then = x.then; //x可能是别人写的promise,那么取then有风险
if (typeof then === "function") {
then.call(x, y => {
if (called) return;
called = true;
resolvePromise(y, promise2, resolve, reject); // 递归解析直到我们的y的值是一个普通值
}, r => {
if (called) return;
called = true;
reject(r);
})
} else { // 没有then方法都执行这里
resolve(x); // 这里x只是一个普通对象
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
// 这里x只是一个普通的值,直接把x传给promise2即可
resolve(x);
}
// 所有promise都要遵循这个规范,这样就可以保证不同人写的promise可以混用
// 核心就在这个resolvePromise方法中
}
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
}
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulfilledCallback, onRejectedCallback) {
// 有可能这个onFulfilledCallback,onRejectedCallback是可选的,所以用户没填,我们要自己给它补上(穿透特性)
onFulfilledCallback = typeof onFulfilledCallback === "function" ? onFulfilledCallback : function (data) {
return data;
};
onRejectedCallback = typeof onRejectedCallback === "function" ? onRejectedCallback : err => {
throw err;
};
let promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilledCallback(this.value);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejectedCallback(this.reason);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === PENDING) {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilledCallback(this.value);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejectedCallback(this.reason);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
catch(errCallback) {
return this.then(null, errCallback);
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}
static resolve(value) {
return new Promise((resolve, reject) => {
resolve(value);
});
}
static all(values) {
return new Promise((resolve, reject) => {
let times = 0;
const arr = [];
function processMap(key, value) {
arr[key] = value;
if (++times === values.length) {
resolve(arr);
}
}
for (let i = 0; i < values.length; i++) {
let val = values[i]; // 可能是promise,也可能是普通值
let then = val && val.then;
if (typeof then === "function") {
then.call(
val,
data => {
// 获取成功的结果
processMap(i, data);
},
reject
);
} else {
processMap(i, val);
}
}
});
}
static race(values) {
return new Promise((resolve, reject) => {
for (let i = 0; i < values.length; i++) {
let p = values[i]; // p可能是promise 也可能是普通值
// 无论谁先成功就成功 谁先失败就失败
if (p instanceof Promise) {
p.then(resolve, reject);
} else {
Promise.resolve(p).then(resolve, reject);
}
}
});
};
finally(cb) {
return this.then(
y => {
return Promise.resolve(cb()).then(() => y);
},
r => {
return Promise.resolve(cb()).then(() => {
throw r;
});
}
);
}
}
module.exports = Promise;