手写Promise| 青训营笔记

69 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的的第3天

Promise

基本使用

let promise = new Promise((resolve, reject)=>{
    resolve("ok")
    // reject("no")
})
promise.then((data)=>{
    consolve.log(data)
},(err)=>{
    consolve.log(err)
})

特点

  1. Promise是一个类,需要new操作符实例化
  2. Promise实例有三个状态,pending、resoved和rejected
  3. 状态只有在pending时才可以进行改变, 即只能改变一次
  4. then有两个参数,一个是成功的回调函数、一个是失败的回调函数

初步实现

// 不能链式调用且不能异步操作的版本
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function Promise(excutor){
    this.state = PENDING
    this.successData = undefined
    this.errorData = undefined
    this.next = null
    this.resolveList = []
    this.rejectList = []
    this.resolve = (value)=>{
        if(this.state != PENDING) return
        this.state = RESOLVED
        this.successData = value
        this.resolveList.forEach(fn=>fn())
    }
    this.reject = (error)=>{
        if(this.state != PENDING) return
        this.state = REJECTED
        this.errorData = error
        this.rejectList.forEach(fn=>fn())
    }
    this.then = (resolveFn, rejectFn)=>{
        if(resolveFn) this.resolveList.push(()=>{
            resolveFn(this.successData)
        })
        if(rejectFn) this.rejectList.push(()=>{
            rejectFn(this.errorData)
        })
    }
    try{
        excutor(this.resolve, this.reject)
    }catch(e){
        this.reject(e)
    }
}


module.exports = Promise

链式调用前言

在实现Promise链式调用之前,让我们先来了解一下两个设计模式

发布订阅模式

简单实现一个发布订阅模式

const event = {
    // 订阅收集
    callbacks: []
    // 发布的数据
    data: {}
    // 订阅的方法
    on(fn){
        this.callbacks.push(fn)
        // console.log("订阅成功")
    },
    // 发布的方法(实际的函数签名依据实际情况确定)
    emit(key, value){
        this.data[key] = value
        // 发布时执行每一个订阅的方法
        this.callbacks.forEach(fn=>{
            fn(this.data)
        })
    }
}

完成event对象的编写后,我们只需要通过event.on方法进行订阅,和通过event.emit()发布,就能完成一个发布订阅模式的简单应用了

event.on((data)=>{
    console.log("订阅A:"data)
})
    event.emit("ask", "有人订阅了我吗?") //output: 订阅A:{"ask", "有人订阅了我吗?"}

观察者模式

简单实现一个观察者模式

// 被观察者
class Subject{
    constructor(name){
        this.name = name
        this.state = "sad"
        // 用一个数组收集订阅者
        this.observers = []
    }
    // 收集观察者对象
    ottch(ob){
        this.observers.push(ob)
    }
    // 更新状态
    setstate(state){
        this.state = state
        // 通知每一位订阅者
        this.observers.forEach(ob=>ob.update(state))
    }
}
class Observer{
    constructor(name){
        this.name = name
    }
    update(state){
        console.log(this.name+"观察到状态变化:"+state)
    }
}

简单应用

var sub = new Subject("sub")
var ob1 = new Object("ob1")
var ob2 = new Object("ob2")
sub.ottch(ob1)
sub.ottch(ob2)
sub.update("happy") // output: ob1观察到状态变化:happy  ob2观察到状态变化:happy

总结

观察者模式需要把观察者注册到被观察者身上去。通过被观察者自身状态的变化通知观察者自身状态变了。 所以,从这里可以看出。发布跟订阅是没有关系的。完全可以只订阅不发布。或者只发布不订阅都是OK的。但是,观察者模式的观察者跟被观察者之间是有关系的。其实,观察者模式是包含发布订阅模式的。原理都是将待执行的方法存储起来。等到状态变化或者被发布出去的时候遍历存储方法的数组进行方法的执行。

链式调用

  1. then方法中的成功回调或者失败的回调,如果返回的是一个Promise,产生的成功(或失败)的值,走下一个then中的成功(或失败)回调
  2. then方法中成功或失败的回调,如果返回的是一个非Promise值,则直接走下一个then的成功回调
  3. then方法中成功或失败的回调,如果产生异常,则直接走下一个then的失败回调
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function Promise(excutor){
    this.state = PENDING
    this.callbacks = []
    this.data = undefined
    this.resolve = (data)=>{
        if(this.state != PENDING) return
        this.state = RESOLVED
        this.data = data
        this.callbacks.forEach(cb=>{
            cb.onResolved(data)
        })
    }
    this.reject = (data)=>{
        if(this.state != PENDING) return
        this.state = REJECTED
        this.data = data
        this.callbacks.forEach(cb=>{
            cb.onRejected(data)
        })
    }
    this.then = (onResolved, onRejected)=>{
        let that = this
        // 为了实现链式调用,果onResolved或onRejected不是函数,则继续将Promise的决议值传递
        onResolved = typeof onResolved == "function" ? onResolved : value=>value
        onRejected = typeof onRejected == "function" ? onRejected : value=>value
        // then通过返回新的Promise来实现链式调用
        return new Promise((resolve, reject)=>{
            // 新的Promise将自己的状态交给handle函数来决定
            function handle(callback){
                try{
                    let result = callback(that.data)
                    // 如果当前这个Promise对象的决议值是一个Promise,则将决议值更改为后者的决议值
                    if(result instanceof Promise) result.then((e)=>{
                        resolve(e)
                    }, (e)=>{
                        reject(e)
                    })
                    else resolve(result)
                }catch(e){
                    reject(e)
                }
            }
            if(this.state == PENDING){
                // 新的Promise对象订阅当前Promise对象的决议,这是最重要的一步
                this.callbacks.push({onResolved(){
                    handle(onResolved)
                }, onRejected(){
                    handle(onRejected)
                }})
            }
            else if(this.state == RESOLVED){
                handle(onResolved)
            }
            else if(this.state == REJECTED){
                handle(onRejected)
            }
        })
    }
    try{
        excutor(this.resolve, this.reject)
    }catch(e){
        this.reject(e)
    }
}

module.exports = Promise

总结:链式调用,就是将.then()内的回调函数交给调用它的promise对象,并且then返回的promise对象会订阅调用then的promise对象的resolve和reject事件,当调用then的promise对象执行resolve或reject时,then返回的promise对象的excutor就会开始执行