来源
前提知识
高阶函数
高阶函数
满足以下条件之一:
- 函数的参数是一个 函数
- 函数的返回值是一个函数
闭包
定义: 有权访问另一个函数作用域中变量的函数(来源红宝书)函数柯里化
柯里化(Currying),把接受多个参数的函数转换成接受一个单一参数的函数举例
```javascript let add = function(x) {
}return function(y) {return x + y}
add(3)(4) // 7 复制代码
实际开发如果需要用到 **柯里化**,推荐使用[ lodash.curry](https://lodash.com/docs/4.17.11#curry)<a name="mVxH3"></a>## 应用(类型判断)`typeof` 无法判断 **对象** 类型<br />`constructor` 判断 是谁构造出来的<br />`instanceof` 判断 谁是谁的实例(即引用类型)<br />`Object.prototype.toString.call()` 完美判断```javascriptfunction 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] = valuethis._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 有直接关系// 一个依赖改变,另一个也改变 Vueclass Observer { // 观察者constructor(name) { // 传递参数this.name = name}update(baby){console.log(this.name+'知道'+baby.name+baby.state)}}class Observed{ // 被观察者constructor(name) {this.name = namethis.state = '开心'this.Observer = []}addObserver(o){this.Observer.push(o)}setState(state){this.state = statethis.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()如果是这样 下面的输出为 undefinedresolve('成功')// 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 = valuethis.state = Success}}let reject = (err)=>{if (this.state === Pending){this.value = errthis.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)breakcase Error:onrejected(this.err)break}}}// export default WritePromise 浏览器端module.exports = WritePromise复制代码
在new的过程中,执行constructor,传入的 resolveorreject,进行赋值和状态改变。然后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. 失败复制代码
异步实现
大家会发现。如果在resolveorreject,执行异步代码(例如定时器)。会发现没有结果。这是因为我们刚才写的都是同步代码。现在要改一下,改成异步的
这时候就用到我们前面的知识了,发布订阅模式
思路
首先,我们应该知道在`constructor`中传入的`fn`,如果加上定时器的话,它的状态`state`不会发生任何改变。也就是一直处于`等待状态`, 所以并不会执行`then`里面的函数。所以我们应该考虑一下当他处于`等待`的时候。是不是应该吧传入的函数存储起来,等到上面执行`resolve`or`reject`的时候,再把这个函数执行。
实现
// promise 异步const Pending = 'PENDING'const Success = 'SUCCESS'const Error = 'Error'class WritePromiseAsync {constructor(fn) {this.state = Pendingthis.value = undefinedthis.err = undefined// 回调函数的存储this.SuccessCal = []this.ErrorCal = []let resolve = (value)=>{if (this.state === Pending){this.value = valuethis.state = Success// 对回调函数进行变量 然后执行this.SuccessCal.forEach((fn)=>fn())}}let reject = (err)=>{if (this.state === Pending){this.value = errthis.state = Errorthis.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)breakcase Error:onrejected(this.err)breakcase Pending:this.SuccessCal.push(()=>{// 为什么要这样写 因为这样可以做一些逻辑 AOP// 这里面可以做一些逻辑onfulfilled(this.value)})this.ErrorCal.push(()=>{onrejected(this.err)})break}}}module.exports = WritePromiseAsync复制代码
在顺一遍。 创建对象之后,调用then方法, 代码开始执行,执行到then的时候,发现没有对应的状态改变,就先把它存储起来。等到定时器结束之后,在把所有的函数都执行一次
链式调用
- 每次调用返回的都是一个新的Promise实例(这就是为什么可以一直
then) - 链式调用的参数通过返回值传递
如果返回的不是普通值,是promise,则会使用这个promise的结果// 第二点的 代码解释let b = new Promise((resolve, reject) => {resolve('data')}).then((data)=>{data = data + '132456'// then可以返回一个值,如果是普通值。就会走到下一个then 的成功中return data}).then((data)=>{console.log(data) // 输出 data132456})复制代码
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 xswitch (this.state) {case Success:x = onfulfilled(this.value)resolve(x)breakcase Error:x = onrejected(this.value)reject(x)breakcase 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'){// 处理promisetry {let then = x.thenif (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 xswitch (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的执行结果作为参数传递给 相应的处理函数breakcase Error:setTimeout(() => {try {x = onrejected(this.err)// reject(x)resolvePromise(promise2, x, resolve, reject)} catch (e) {reject(e)}}, 0)breakcase 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') {// 处理promisetry {let then = x.thenif (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=>vonrejected = 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.thenif (typeof then === 'function') {then.call(x, y => {if (called) returncalled = trueresolvePromise(promise2, y, resolve, reject)}, r => {if (called) return // 如果发现被调用过 直接returncalled = truereject(r)})} else {resolve(x)}} catch (e) {if (called) returncalled = truereject(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 calledif ((typeof x === 'object' && x != null) || typeof x === 'function') {try {let then = x.thenif (typeof then === 'function') {then.call(x, y => {if (called) returncalled = trueresolvePromise(promise2, y, resolve, reject)}, r => {if (called) returncalled = truereject(r)})} else {resolve(x)}} catch (e) {if (called) returncalled = truereject(e)}} else {resolve(x)}}class Promise {constructor(executor) {this.status = PENDINGthis.value = undefinedthis.reason = undefinedthis.SuccessCal = []this.ErrorCal = []let resolve = value => {if (this.status === PENDING) {this.value = valuethis.status = RESOLVEDthis.SuccessCal.forEach(fn => fn())}}let reject = reason => {if (this.status === PENDING) {this.reason = reasonthis.status = REJECTEDthis.ErrorCal.forEach(fn => fn())}}try {executor(resolve, reject)} catch (e) {reject(e)}}then(onRESOLVED, onrejected) {onRESOLVED = typeof onRESOLVED === 'function' ? onRESOLVED : v => vonrejected = 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 = resolvedfd.reject = reject})return dfd}module.exports = Promise复制代码
关于符合性测试
这个是测试工具的github
安装之后, 执行npx promises-aplus-tests promise.js
为什么是npx? 我没有全局安装
全部通过
