从0开始。从头开始手写Promise。

前提知识

高阶函数

高阶函数

知足如下条件之一:git

  1. 函数的参数是一个 函数
  2. 函数的返回值是一个函数

闭包

定义: 有权访问另外一个函数做用域中变量的函数(来源红宝书)github

函数柯里化

柯里化(Currying),把接受多个参数的函数转换成接受一个单一参数的函数编程

举例

let add = function(x) {
        return function(y) {
            return x + y
        }
    }
    
add(3)(4)       // 7
复制代码

实际开发若是须要用到 柯里化,推荐使用 lodash.curry 设计模式

应用(类型判断)

typeof 没法判断 对象 类型数组

constructor 判断 是谁构造出来的promise

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的第一个函数,rejectthen的第二个函数。而且都将参数传入。

**注意:**走成功了就不能够走失败,反之亦然

从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,传入的 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里面的函数。因此咱们应该考虑一下当他处于等待的时候。是否是应该吧传入的函数存储起来,等到上面执行resolveorreject的时候,再把这个函数执行。

实现

// 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,只须要进行一个递归就能够解决

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
相关文章
相关标签/搜索