哈哈,好久没写博客了,主要是因为这些日子刚入职了网易,一直在适应工作,还有学校里各种乱七八糟的琐事,所以一直没有来得及写。今天刚好做完了一个迭代,难得有空闲时间来写篇博客(其实是摸鱼~)。
之前我也写过一篇手撕promise,只不过那篇只有代码,没有解释,所以不太容易理解。这篇我准备先从理论说起,再配合代码进行介绍。
哈哈哈哈,就不说废话了,直接进入正题。
高阶函数的概念:
(1)一个函数的参数是一个函数,我们可以成这个函数为高阶函数。
(2)一个函数返回一个函数,我们可以称这个函数为高阶函数。
(3)符合以上任意一点,我们就可以称这个函数为高阶函数。
概念(摘自菜鸟教程):
装饰器模式(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
我之前写过一篇柯里化函数的博客,简单来说,柯里化函数就是利用了闭包的预存储的功能,它将一个多个参数的函数,转化成一个个参数传入的函数。
举个例子:
// 原本fn有四个参数,a,b,c,d function fn(a,b,c,d){...} // 通过柯里化,可以转化成 fn(a)(b)(c)(d)
发布订阅模式是前端中一种常见的设计模式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(); });
观察者模式中有一个观察者和一个被观察者,被观察者有一个自身状态,当自身状态改变了,通知所有观察者执行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('老师表扬我了 开心了')
有了上面的基础,我们进行手写promise就会容易些了
首先根据promise的定义:
我们来写一个低配版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;
接下来让我们完善一下我们写的promise,根据promiseA+规范:
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+规范:
在这里插入图片描述
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;
附上一个通过测试的截图
加入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;