前提知识
高阶函数
高阶函数
知足如下条件之一:前端
-
函数的参数是一个 函数git
-
函数的返回值是一个函数github
闭包
定义: 有权访问另外一个函数做用域中变量的函数(来源红宝书
)编程
函数柯里化
柯里化(Currying),把接受多个参数的函数转换成接受一个单一参数的函数设计模式
举例
let add = function(x) { return function(y) { return x + y } } add(3)(4) // 7
实际开发若是须要用到 柯里化,推荐使用 lodash.curry数组
应用(类型判断)
typeof
没法判断 对象 类型promise
constructor
判断 是谁构造出来的浏览器
instanceof
判断 谁是谁的实例(即引用类型)微信
Object.prototype.toString.call()
完美判断闭包
function isType(type) { return function (content) { return Object.prototype.toString.call(content) === `[object ${type}]` } } let isString = isType('String') console.log(isString('132456')) // 函数柯里化 console.log(isType('Number')(132456))
AOP 面向切片编程
也成为 装饰者模式
定义:
指在不修改原有代码的状况下增长新功能
function sleep(who) { who?console.log(who + '睡觉'):console.log('睡觉') } Function.prototype.before = function (callback) { return (...args)=> {// args 传入的参数数组 callback() args?this(...args):this() // 传入参数 } } let Wash = sleep.before(function () { console.log('洗脸') }) Wash() // 洗脸 睡觉 Wash('我') // 洗脸 我睡觉
观察者模式
订阅发布
有挣议
,有些人说这个不算是观察者模式。我的以为算是属于
let e = { _obg:{}, // 事件 _callback:[], // 处理函数列表 on(callback) { // 订阅 this._callback.push(callback) }, emit(key,value){ // 发布 this._obg[key] = value this._callback.forEach(fn=>{ fn(this._obg) // 参数传入 }) } } e.on(function (obj) { console.log('发布一个') console.log(obj) }) setTimeout(function () { e.emit('name','琛') },1000)
举一个简单的例子,即微博,你关注了 A,A 发动态就会通知你。你和 A 没有直接联系,经过 微博本身的调度来完成
观察者模式
与发布订阅区别
发布订阅模式
是 二者之间没有直接关系,经过实践调度中心来完成。而观察者模式
是相互依赖的,一个改变。另外一个也发生改变
例子
// 设计模式 观察者模式 // 与发布订阅二者区别 // 发布订阅 是基于一个中间管理 on 和 emit 没有直接关系 // 观察者模式 是 Observer and Observed 有直接关系 // 一个依赖改变,另外一个也改变 Vue class Observer { // 观察者 constructor(name) { // 传递参数 this.name = name } update(baby){ console.log(this.name+'知道'+baby.name+baby.state) } } class Observed{ // 被观察者 constructor(name) { this.name = name this.state = '开心' this.Observer = [] } addObserver(o){ this.Observer.push(o) } setState(state){ this.state = state this.Observer.forEach(o=>{ o.update(this) }) } } let baby = new Observed('宝宝') let dad = new Observer('爸爸') let mom = new Observer('妈妈') // 添加观察者 baby.addObserver(dad) baby.addObserver(mom) // 设置状态 baby.setState('不开心') baby.setState('开心')
进入正题
基本的 promise 使用
promise
用来解决异步
Promise 是一个天生的类 须要传入一个函数 默认会当即执行
有三种状态 ,成功(resolve) ,失败(reject), 等待
基本用法
let a = new Promise((resolve, reject) => { // 这两个方法能够更改promise 状态 // resolve()若是是这样 下面的输出为 undefined resolve('成功') // reject('失败') }) a.then((data)=>{ console.log(data) // 成功 },(err)=>{ console.log(err) // 失败 })
resolve,reject 这两个方法能够改变状态。若是是 resolve
,则走then
的第一个函数,reject
走then
的第二个函数。而且都将参数传入。
**注意:**走成功了就不能够走失败,反之亦然
从 0 开始,手写 Promise
明白了基本用法,咱们开始模仿一下Promise
。这里使用ES6
语法来模仿。
这是 Promise 的规范
同步实现
首先,须要就收一个executor
,是一个函数。当即执行,有三那种状态,里面还有resolve
,reject
两个参数,这两个参数是函数,须要接收两个参数。同时promise
还有then
方法
思路分析
// promise 同步 // 三种状态的定义 const Pending = 'PENDING' const Success = 'SUCCESS' const Error = 'Error' // promise 的实现 class WritePromise { constructor(fn) { // 初始化 this.state = Pending // 状态初始化 this.value = undefined // 成功信息 this.err = undefined // 错误信息 let resolve = (value)=>{ if (this.state === Pending){ this.value = value this.state = Success } } let reject = (err)=>{ if (this.state === Pending){ this.value = err this.state = Error } } // 可能会出错 try { fn(resolve,reject) // 当即执行 }catch (e) { console.log(e) // 若是内部出错 直接交给reject 方法向下传递 reject(e) } } then(onfulfilled,onrejected){ switch (this.state) { case Success: onfulfilled(this.value) break case Error: onrejected(this.err) break } } } // export default WritePromise 浏览器端 module.exports = WritePromise
在 new 的过程当中,执行constructor
,传入的 resolve
orreject
,进行赋值和状态改变。而后then
方法更具 state
的状态进行不一样的操做。onfulfilled
and onrejected
是传入的操做函数
为何要加 try catch
在使用过程当中,不只仅只有reject
能够接收错误,也能够手动抛出错误。这样就reject
捕获不到错误。因此要加上 try catch 。保证能够正常运行
测试
let WritePromise = require('./WritePromise') let promise = new WritePromise((resolve, reject) => { // 1. resolve('成功') // 2. // reject('失败') }) promise.then((data) => { // onfulfilled 成功 console.log(data) }, (err) => { // onrejected 失败 console.log(err) }) // 输出 1. 成功 2. 失败
异步实现
你们会发现。若是在resolve
orreject
,执行异步代码(例如定时器)。会发现没有结果。这是由于咱们刚才写的都是同步代码。如今要改一下,改为异步的
这时候就用到咱们前面的知识了,发布订阅模式
思路
首先,咱们应该知道在constructor
中传入的fn
,若是加上定时器的话,它的状态state
不会发生任何改变。也就是一直处于等待状态
, 因此并不会执行then
里面的函数。因此咱们应该考虑一下当他处于等待
的时候。是否是应该吧传入的函数存储起来,等到上面执行resolve
orreject
的时候,再把这个函数执行。
实现
// promise 异步 const Pending = 'PENDING' const Success = 'SUCCESS' const Error = 'Error' class WritePromiseAsync { constructor(fn) { this.state = Pending this.value = undefined this.err = undefined // 回调函数的存储 this.SuccessCal = [] this.ErrorCal = [] let resolve = (value)=>{ if (this.state === Pending){ this.value = value this.state = Success // 对回调函数进行变量 而后执行 this.SuccessCal.forEach((fn)=>fn()) } } let reject = (err)=>{ if (this.state === Pending){ this.value = err this.state = Error this.ErrorCal.forEach((fn)=>fn()) } } // 可能会出错 try { fn(resolve,reject) // 当即执行 }catch (e) { console.log(e) // 若是内部出错 直接交给reject 方法向下传递 reject(e) } } then(onfulfilled,onrejected){ switch (this.state) { case Success: onfulfilled(this.value) break case Error: onrejected(this.err) break case Pending: this.SuccessCal.push(()=>{ // 为何要这样写 由于这样能够作一些逻辑 AOP // 这里面能够作一些逻辑 onfulfilled(this.value) }) this.ErrorCal.push(()=>{ onrejected(this.err) }) break } } } module.exports = WritePromiseAsync
在顺一遍。建立对象以后,调用then
方法, 代码开始执行,执行到then
的时候,发现没有对应的状态改变,就先把它存储起来。等到定时器结束以后,在把全部的函数都执行一次
链式调用
-
每次调用返回的都是一个 新的 Promise 实例(这就是为何能够一直
then
) -
链式调用的参数经过返回值传递
// 第二点的 代码解释 let b = new Promise((resolve, reject) => { resolve('data') }).then((data)=>{ data = data + '132456' // then能够返回一个值,若是是普通值。就会走到下一个then 的成功中 return data }).then((data)=>{ console.log(data) // 输出 data132456 })
若是返回的不是普通值,是 promise,则会使用这个 promise 的结果
let b = new Promise((resolve, reject) => { resolve('data') }).then((data)=>{ data = data + '132456' return data }).then(()=>{ return new Promise((resolve, reject) => { resolve('我是promise 的返回') // 若是返回的是一个promise,那么会采用这个promise的结果 }) }).then((data)=>{ console.log(data) // 输出 我是promise 的返回 })
catch
用来捕获 最近的且没有捕获的错误
let b = new Promise((resolve, reject) => { reject('data') }).then().catch(err=>{ // 捕获错误 捕获最近的没有捕获的错误 console.log(err+'catch') // datacatch // 注意 返回的也是undefined })
注意点
上述走的是成功
,失败也同样。但会有一个小坑。
let b = new Promise((resolve, reject) => { resolve('data') }).then(()=>{},err=>{ console.log(err) // 在失败函数中若是返回的是一个普通值,也会走下一次then的成功中 // return undefined 至关于返回了一个这个 }).then((data)=>{ console.log(data+'success') // 这个会走 成功的值 输出 underfinedsuccess },(err)=>{ console.log(err+'err') })
特别注意,这里会常常有遗漏。
链式调用的手写实现
接着上次的WritePromiseAsync
实现屡次 then 传递 思路
原版作法中,当连续调用then
方法的时候,会把上一次的结果传递给下一个then
。
上面说过每次调用then
方法会返回一个promise
实例。因此,咱们须要在调用then
方法的时候返回一个promise
的实例,而且接收到then
方法的结果。在传递给这个promise
// 多余的我就不写了,主要写差别化 的 then方法 then(onfulfilled, onrejected) { let promise2 = new ChainPromise((resolve, reject) => { let x switch (this.state) { case Success: x = onfulfilled(this.value) resolve(x) break case Error: x = onrejected(this.value) reject(x) break case Pending: this.SuccessCal.push(() => { try { let x = onfulfilled(this.value) resolve(x) } catch (e) { reject(e) } }) this.ErrorCal.push(() => { try { let x = onrejected(this.err) reject(x) } catch (e) { reject(e) } }) break } }) return promise2 }
注意,调用的时候要把 then
的两个函数都要写上,不然会报错(尚未处理)
这样事后 就能够实现 屡次 then 方法传递结果了
实现 返回 promise 思路
说一下上面得哪一个x
,咱们是直接把它返回给对应得处理方法,若是x
是一个promise
呢?按照原版得来讲。咱们应该把这个promise
的结果做为返回值来继续传递。因此咱们应该对这个x
进行处理
建立一个方法solveX
,来处理x
。
function solveX(promise2, x, resolve, reject) { if (promise2 === x){ return reject(new TypeError('引用自己')) } if ((typeof x === 'object' && x != null)|| typeof x === 'function'){ // 处理promise try { let then = x.then if (typeof then === 'function'){ // 只能认定他是promise了 then.call(x,(data)=>{ console.log(data) resolve(data) },(err)=>{ reject(err) }) } else { resolve(x) } } catch (e) { reject(e) // 取值失败 走err } }else { // 是一个普通值 resolve(x) } }
为何要把promise2
传进来呢?由于若是 x
就是promise2
呢?则会是一个死循环。
对x
进行判断,若是是普通值,直接返回就能够了。若是不是,咱们取then
方法(注意是方法,不是结果). 若是有这个方法,咱们就认定他是一个promise
(可能有人会说若是then
是一个空方法呢?,那也只能认定了,咱们最多只能作到这种程度的判断了。)
注意then
的 this 指向问题
关于 promise2 传入问题
try { x = onfulfilled(this.value) resolvePromise(promise2, x, resolve, reject) // 须要用x 来比较promise2的值 // resolve() } catch (e) { // 一旦出错,走下一个promise 的错误处理方法 reject(e) }
若是直接传入promise2
的话,由于是同步的过程,在建立的时候promise2
尚未生成,因此会报错。这时候咱们能够加一个定时器,把它变成异步。这就解决了这个问题
then(onfulfilled, onrejected) { let promise2 = new ChainPromise((resolve, reject) => { let x switch (this.state) { case Success: setTimeout(() => { // 若是不加定时器,promise2获取不到 try { x = onfulfilled(this.value) resolvePromise(promise2, x, resolve, reject) // 须要用x 来比较promise2的值 // resolve() } catch (e) { // 一旦出错,走下一个promise 的错误处理方法 reject(e) } }, 0) // 实现以后要判断 X 若是x是一个普通值,就正常返回。若是是一个promise 则把promise的执行结果做为参数传递给 相应的处理函数 break case Error: setTimeout(() => { try { x = onrejected(this.err) // reject(x) resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }, 0) break case Pending: this.SuccessCal.push(() => { try { let x = onfulfilled(this.value) resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) this.ErrorCal.push(() => { try { let x = onrejected(this.err) resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) break } }) return promise2 }
注意,即便写的是0
,也不会当即执行。
解决 then 里面继续返回 promise
上面咱们写了一个方法来处理promise
,只须要进行一个递归就能够解决
/** * * @param promise2 * @param x * @param resolve * @param reject * @returns {*} */ function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) { return reject(new TypeError('引用自己')) } if ((typeof x === 'object' && x != null) || typeof x === 'function') { // 处理promise try { let then = x.then if (typeof then === 'function') { then.call(x, (data) => { // resolve(data) 将data从新放入这个函数。直到是一个普通值再进行返回 resolvePromise(promise2, data, resolve, reject) }, (err) => { // reject(err) resolvePromise(promise2, err, resolve, reject) }) } else { resolve(x) } } catch (e) { reject(e) // 取值失败 走err } } else { // 是一个普通值 resolve(x) } }
解决 then 必须传值
then(onfulfilled, onrejected) { onfulfilled = typeof onfulfilled === 'function'?onfulfilled:v=>v onrejected = typeof onrejected === 'function'?onrejected:err=>{throw err} ........ }
将两个函数进行判断。若是不是函数,默认赋一个函数
防止屡次调用
function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) { return reject(new TypeError('Chaining cycle detected for promise #<Promise>--')) } let called // 添加一个变量进行控制 if ((typeof x === 'object' && x != null) || typeof x === 'function') { try { let then = x.then if (typeof then === 'function') { then.call(x, y => { if (called) return called = true resolvePromise(promise2, y, resolve, reject) }, r => { if (called) return // 若是发现被调用过 直接return called = true reject(r) }) } else { resolve(x) } } catch (e) { if (called) return called = true reject(e) } } else { resolve(x) } }
总结代码
// promise 链式调用的实现 const PENDING = 'PENDING' const RESOLVED = 'RESOLVED ' const REJECTED = 'REJECTED' function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) { return reject(new TypeError('Chaining cycle detected for promise #<Promise>--')) } let called if ((typeof x === 'object' && x != null) || typeof x === 'function') { try { let then = x.then if (typeof then === 'function') { then.call(x, y => { if (called) return called = true resolvePromise(promise2, y, resolve, reject) }, r => { if (called) return called = true reject(r) }) } else { resolve(x) } } catch (e) { if (called) return called = true reject(e) } } else { resolve(x) } } class Promise { constructor(executor) { this.status = PENDING this.value = undefined this.reason = undefined this.SuccessCal = [] this.ErrorCal = [] let resolve = value => { if (this.status === PENDING) { this.value = value this.status = RESOLVED this.SuccessCal.forEach(fn => fn()) } } let reject = reason => { if (this.status === PENDING) { this.reason = reason this.status = REJECTED this.ErrorCal.forEach(fn => fn()) } } try { executor(resolve, reject) } catch (e) { reject(e) } } then(onRESOLVED, onrejected) { onRESOLVED = typeof onRESOLVED === 'function' ? onRESOLVED : v => v onrejected = typeof onrejected === 'function' ? onrejected : err => { throw err } let promise2 = new Promise((resolve, reject) => { if (this.status === RESOLVED) { setTimeout(() => { // 若是不加定时器,promise2获取不到 try { let x = onRESOLVED(this.value) resolvePromise(promise2, x, resolve, reject) // 须要用x 来比较promise2的值 } catch (e) { // 一旦出错,走下一个promise 的错误处理方法 reject(e) } }, 0) // 实现以后要判断 X 若是x是一个普通值,就正常返回。若是是一个promise 则把promise的执行结果做为参数传递给 相应的处理函数 } if (this.status === REJECTED) { setTimeout(() => { try { let x = onrejected(this.reason) resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }, 0) } if (this.status === PENDING) { this.SuccessCal.push(() => { // 为何要这样写 由于这样能够作一些逻辑 // 这里面能够作一些逻辑 setTimeout(() => { try { let x = onRESOLVED(this.value) resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }, 0) }) this.ErrorCal.push(() => { setTimeout(() => { try { let x = onrejected(this.reason) resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }, 0) }) } }) return promise2 } } // 测试 须要测试再添加 Promise.defer = Promise.deferred = function () { let dfd = {} dfd.promise = new Promise((resolve, reject) => { dfd.resolve = resolve dfd.reject = reject }) return dfd } module.exports = Promise
关于符合性测试
这个是测试工具的github
安装以后, 执行
npx promises-aplus-tests promise.js
为何是npx
? 我没有全局安装
所有经过

测试结果.png
本文分享自微信公众号 - 阿琛前端成长之路(lwkWyc)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。