从零开始 手写完整的Promise原理

183 阅读4分钟
  • 掌握高阶函数的使用,使用高阶函数解决异步问题

  • 掌握发布订阅模式和观察者模式

  • 掌握Promise核心应用。使用Promise解决异步编程问题

  • 实现一个完整的promise库

  • 掌握Promise中常见的面试题

  • 扩展Promise的常见方法 all,rece.finally

  • 掌握generator的使用以及co库的使用

  • 异步的终极解决方案async+await

Callback

什么是高阶函数?

  1. 如果一个函数的参数是一个函数(回调函数是一种高阶函数)

  2. 如果一个函数返回一个函数 当前函数也是一种高阶函数

    function say(a,b){ console.log('say',a,b); } Function.prototype.before=function(callback){ return (...args)=>{ callback(); this(...args) } } let beforeSay=say.before(function(){ console.log('before say') }) beforeSay('hello',''world)

函数柯里化

什么是函数柯里化?函数柯里化是将一个大的范围的函数通过返回函数封装来进行更细一步的范围缩小

  • 函数柯里化

  • 函数反柯里化

  • 判断变量的类型

  • 常用的判断变量类型的方法

  • typeof 不能判断对象的类型 typeof[] || typeof()

  • constructor  可以找到这个变量是通过谁构造出来的

  • instanceof 判断对象是谁的实例 __proto__

  • Object.prototype.toString.call() 缺陷就是不能细分谁是谁的实例

    function isType(type){ return function(value){ return Object.prototype.toString.call(value)===[object ${type}]
    } } let isArray=isType('Array') console.log(isArray('hello')) console.log(isArray([]))

    const currying=(fn,arr=[])=>{ let len=fn.length return function(...args){ arr=[...arr,...args] if(arr.length<len){ return currying(fn,arr) }else{ return fn(...arr) } } } function sum(a,b,c,d,e,f){ return Math.sum(...args) } console.log(currying(sum)(1,2)(3,4)(5)(6))

通过回调函数异步解决并发问题

多个异步请求如何同时获取最终结果

let fs=require('fs')
let school={}
let index=0
const cb=()=>{
    if(++index==2){
        console.log(school)    
    }
}
fs.readFile('./name.txt','utf8',function(arr,data){
    school.name=data
    cb()
})
fs.readFile('./age.txt','utf8',function(arr,data){
    school.age=data
    cb()
})

进一步优化之后我们可以得到以下结果

function after(times,callback){
    return function(){
        if(--times==0){
            callback()
        }
    }
}
let cb=after(2,function(){
    console.log(school)
})
fs.readFile('./name.txt','utf8',function(arr,data){
    school.name=data
    cb()
})
fs.readFile('./age.txt','utf8',function(arr,data){
    school.age=data
    cb()
})

发布订阅模式

发布订阅模式主要分成两个部分,主要分成on和emit

  • on主要是吧一些函数维护到一个数组当中去

  • emit就是让数组中的方法依次执行

    let fs=require('fs') let event={ //订阅和发布没有明显的关联 arr:[], on(){ this.arr.push(fn) }, emit(){ this.arr.forEach(fn=>fn()) } } event.on(function(){ console.log("读取了一个") }) event.on(function(){ if(Object.keys(school).length==2){ console.log('school') } }) fs.readFile('./name.txt','utf8',function(arr,data){ school.name=data event.emit() }) fs.readFile('./age.txt','utf8',function(arr,data){ school.age=data event.emit() })

观察者模式

有观察者肯定有被观察者,观察者需要放到被观察者中 被观察者的状态发生变化,需要通知观察者。内部也是基于发布订阅模式的 收集观察者,状态一旦改变之后都要通知观察者

class Subject{ //被观察者
    constructor(){
        this.state='开心的'
        this.observers=[]
    }
    attach(o){

    }
    setState(newState){
        this.state=newState
        this.observers.forEach(o=>o.update(this))
    }
}

class Observer{ //观察者
    constructor(name){
        this.name=name
    }
    update(baby){
      console.log("当前"+this.name+"被通知了,",'当前小宝宝的状态是'+baby.state)
    }
}
//我需要观察一个物体的状态,需要观察小宝宝心里状态的变化
let baby=new Subject('小宝宝')
let father=new Observer('爸爸')
let mother=new Observer('妈妈')
baby.attach(father)
baby.attach(mother)
baby.setState('被欺负了')

简单的Promise

  • promise的特点以及概念:promisesplu.com/ promisea+ 规范

  • 然后注意一个特点IE他是不支持promise的。

  • promise为什么会产生?

  • 解决程序异步问题,多个异步请求并发 我希望同步最终的结果,我希望吧A和B的结果一起拿到。Promise.all

  • 为了解决链式异步请求的问题,上一个人的输出是下一个人的输入。Promise的链式调用能够解决这个问题

  • 缺陷:Promise还是基于回调的

  • Promise的三个状态

  • 成功态(resolve)

  • 失败态(reject)

  • 等待态(pending)

  • Promise就是一个类

    //用户自己决定成功的原因和失败的原因 let promise=new Promise((resolve,reject)=>{ resolve('发工资') }) console.log(promise)

实现Promise

console.log(mine)const RESOLVED = 'RESOLVED'; //成功const REJECTED = 'REJECTED'; //失败const PENDING = 'PENDING'; //等待// 只有等待态的时候才能去更改状态class PromiseA {    constructor(executor) {        // Promise的状态        this.status = PENDING;        // 成功的原因        this.value = undefined;        // 失败的原因        this.reason = undefined;        // 这两个函数并没有在实例上,都是各自独有的 所以不在Promise上        let resolve = (value) => { //调用这个方法就是成功            if(this.status==PENDING){                this.value=value            this.status=RESOLVED            }                    }        let reject = (reason) => { //调用这个方法就是失败            if(this.status==PENDING){                this.reason=reason            this.status=REJECTED            }                    }        try{            executor(resolve,reject)        }catch(e){              console.log(e)            reject(e)        }    }    then(onFulfilled,onRejected){        if(this.status===RESOLVED){            onFulfilled(this.value);        }        if(this.status===REJECTED){            onRejected(this.reason)        }    }}module.exports= PromiseA

SetTimeOut调用问题

当Promise调用then方法时 里面写了个SetTimeout。那么此时Promise的状态还是等待状态。此时执行then方法可能不回执行到传入进来的成功或者失败方法。

这里我们就引入发布订阅模式。如果当前状态是pendding的话,我们将成功的回调和失败的回调保存下来。稍后调用resolve和reject的时候重新执行。

所有的函数库都离不开发布订阅模式。只要你的库有异步调用的情况

console.log(mine)const RESOLVED = 'RESOLVED'; //成功const REJECTED = 'REJECTED'; //失败const PENDING = 'PENDING'; //等待// 只有等待态的时候才能去更改状态class PromiseA {    constructor(executor) {        // Promise的状态        this.status = PENDING;        // 成功的原因        this.value = undefined;        // 失败的原因        this.reason = undefined;        // 专门用来存放成功的回调        this.onResolvedCallbacks=[]        // 专门用来存放失败的回调        this.onRejectedCallbacks=[]        // 这两个函数并没有在实例上,都是各自独有的 所以不在Promise上        let resolve = (value) => { //调用这个方法就是成功            if(this.status==PENDING){                this.value=value                this.status=RESOLVED                this.onResolvedCallbacks.forEach(fn=>fn())            }                    }        let reject = (reason) => { //调用这个方法就是失败            if(this.status==PENDING){                this.reason=reason                this.status=REJECTED                this.onRejectedCallbacks.forEach(fn=>fn())            }        }        try{            executor(resolve,reject)        }catch(e){              console.log(e)            reject(e)        }    }    then(onFulfilled,onRejected){        if(this.status===RESOLVED){            onFulfilled(this.value);        }        if(this.status===REJECTED){            onRejected(this.reason)        }        if(this.status==PENDING){            this.onRejectedCallbacks.push(()=>{                onRejected()            })            this.onResolvedCallbacks.push(()=>{                onFulfilled()            })        }    }}module.exports= PromiseA