Promise实现只需要三步,你会了吗?

764 阅读5分钟

先说下为什么要用promise? promise解决了哪些问题? 通常我们的回调都是放到回调函数中解决,会产生回调地狱问题,因此promise出现,解决了回调地狱问题。 promise不仅解决了回调地狱问题,还解决了统一错误处理

第一步简单实现

  • 实现多次then及异步执行,so easy。
心得:resolve或reject方法的执行,只是改变promise的状态status和value值,很简单,不要多想。
const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
class Promise{
    constructor(executor){
        this.value = ''
        this.status = PENDING
        this.onResolvedCallBacks = [] // 初始值为数组,executor为异步执行时,多次then会有多个函数存放
        this.onRejectedCallBacks = []
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.value = value
                this.status = RESOLVED
                this.onResolvedCallBacks.forEach(fn => fn()) // 发布
            }
        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.value = reason
                this.status = REJECTED
                this.onRejectedCallBacks.forEach(fn => fn()) // 发布
            }
        }
        try {
            executor(resolve,reject)
        }catch(e){
            reject(e)
        }
    }
    
    then(onFulfilled, onRejected) {
        if (this.status === RESOLVED) {
            onFulfilled(this.value)
        }
        if (this.status === REJECTED) {
            onRejected(this.value)
        }
        if (this.status === PENDING) { // 订阅
            this.onResolvedCallBacks.push(() => { // 函数中方便处理, 装饰模式,切片编程
                onFullfilled(this.value)
            })
            this.onRejectedCallBacks.push(() => {
                onRejected(this.value)
            })
        }
    }
}
module.exports = Promise
  • 测试一下
  1) 测试异步执行
  new Promise((resolve, reject) => {
      setTimeout(() => {
          resolve(1)
      }, 1000);
  }).then(value => {
      console.log(value)
  })
  // 1

  2) 测试多次then
  let promise = new Promise((resolve, reject) => {
      setTimeout(() => {
          resolve(1)
      }, 1000);
  })
  promise.then(value => {
      console.log('value1: ', value)
  })
  promise.then(value => {
      console.log('value2: ', value)
  })
  //	value1:  1
  //	value2:  1

第二步then的链式调用实现

  • 实现then的链式调用(主要靠then回调方法的返回值)
const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
// 可以看做,此方法主要处理promise2的状态
// then参数方法为一个promise, 通过方法的返回值,来判断下个then的执行
function resolvePromise(x, promise2, resolve, reject) {
    if (x === promise2) {
        reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    // 判断x 来执行resolve或reject
    // 判断x是否为promise:是object类型、有then方法且类型为function
    if (typeof x === 'object' && x !== null || typeof x === 'function') {
        try {
            let then = x.then
            if (typeof then === 'function') {
            	// x为promise
                then.call(x, y => {
                    resolvePromise(y, promise2, resolve, reject)
                }, r => {
                    reject(r)
                })
            } else {
            	// {then: 1}
                resolve(x)
            }
        }catch(e) {
        	// {then: throw new Error('err')}
            reject(e)
        }
    } else {
    	// x为普通值
    	resolve(x)
    }
}
class Promise{
    constructor(executor){
        this.value = ''
        this.status = PENDING
        this.onResolvedCallBacks = [] // 初始值为数组,executor为异步执行时,多次then会有多个函数存放
        this.onRejectedCallBacks = []
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.value = value
                this.status = RESOLVED
                this.onResolvedCallBacks.forEach(fn => fn()) // 发布
            }
        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.value = reason
                this.status = REJECTED
                this.onRejectedCallBacks.forEach(fn => fn()) // 发布
            }
        }
        try {
            executor(resolve,reject)
        }catch(e){
            reject(e)
        }
    }
    
    then(onFulfilled, onRejected) {
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === RESOLVED) {
                setTimeout(() => {
                    try {
                    	// 重点
                        let x = onFulfilled(this.value)
                        resolvePromise(x, promise2, resolve, reject)
                    }catch(e) {
                        reject(e)
                    }
                }, 0)
            }
            if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                    	// 重点
                        let x = onRejected(this.value)
                        resolvePromise(x, promise2, resolve, reject)
                    }catch(e) {
                        reject(e)
                    }
                }, 0)
                
            }
            if (this.status === PENDING) { // 订阅
                this.onResolvedCallBacks.push(() => { // 函数中方便处理
                    setTimeout(() => {
                    	try {
                        	// 重点
                            let x = onFulfilled(this.value)
                            resolvePromise(x, promise2, resolve, reject)
                        }catch(e) {
                            reject(e)
                        }
                    }, 0)
                })
                this.onRejectedCallBacks.push(() => {
                	setTimeout(() => {
                        try {
                        	// 重点
                            let x = onRejected(this.value)
                            resolvePromise(x, promise2, resolve, reject)
                        }catch(e) {
                            reject(e)
                        }
                    }, 0)
                })
            }
        })
        return promise2
    }
}
module.exports = Promise
  • 测试一下
  new Promise((resolve, reject) => {
      resolve(1)
  }).then(value => {
      console.log('value1: ', value)
      return value
  }).then(value => {
      console.log('value2: ', value)
  }, reason => {
      console.log(reason)
  })
  // value1:  1
  // value2:  1

第三步解决几个问题

  • 1.值穿透
  • 2.解决resolve的value为Promise的情况
  • 3.解决引入其他不规范的promise,导致多次调用问题
const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'

function resolvePromise(x, promise2, resolve, reject) {
    if (x === promise2) {
        reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    // 判断x 来执行resolve或reject
    // 判断x是否为promise:是object类型、有then方法且类型为function
    if (typeof x === 'object' && x !== null || typeof x === 'function') {
    	let called; // 解决问题3
        try {
            let then = x.then
            if (typeof then === 'function') {
            	// x为promise
                then.call(x, y => {
                	if(called)return
                    called = true
                    resolvePromise(y, promise2, resolve, reject)
                }, r => {
                    if(called)return
                    called = true
                    reject(r)
                })
            } else {
            	// {then: 1}
                resolve(x)
            }
        } catch(e) {
        	// {then: throw new Error('err')}
            if(called)return
            called = true
            reject(e)
        }
    } else {
    	// x为普通值
        resolve(x)
    }
}
class Promise{
    constructor(executor){
        this.value = ''
        this.status = PENDING
        this.onResolvedCallBacks = [] // 初始值为数组,executor为异步执行时,多次then会有多个函数存放
        this.onRejectedCallBacks = []
        const resolve = (value) => {
            if (this.status === PENDING) {
            	// 如果value为Promise的实例,则递归解析出this.value,解决问题2
                if (value instanceof Promise) {
                    return value.then(resolve, reject)
                }
                this.value = value
                this.status = RESOLVED
                this.onResolvedCallBacks.forEach(fn => fn()) // 发布
            }
        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.value = reason
                this.status = REJECTED
                this.onRejectedCallBacks.forEach(fn => fn()) // 发布
            }
        }
        try {
            executor(resolve,reject)
        }catch(e){
            reject(e)
        }
    }
    
    then(onFulfilled, onRejected) {
    	// 如果then没有形参,则给实参赋默认值函数,接收参数返回参数即可实现值穿透,解决问题1
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
        onRejected = typeof onRejected === 'function' ? onRejected : e => {throw e}
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === RESOLVED) {
                setTimeout(() => {
                    try {
                    	// 重点
                        let x = onFulfilled(this.value)
                        resolvePromise(x, promise2, resolve, reject)
                    }catch(e) {
                        reject(e)
                    }
                }, 0)
            }
            if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                    	// 重点
                        let x = onRejected(this.value)
                        resolvePromise(x, promise2, resolve, reject)
                    }catch(e) {
                        reject(e)
                    }
                }, 0)
                
            }
            if (this.status === PENDING) { // 订阅
               this.onResolvedCallBacks.push(() => { // 函数中方便处理
                    setTimeout(() => {
                    	try {
                        	// 重点
                            let x = onFulfilled(this.value)
                            resolvePromise(x, promise2, resolve, reject)
                        }catch(e) {
                            reject(e)
                        }
                    }, 0)
                })
                this.onRejectedCallBacks.push(() => {
                	setTimeout(() => {
                    	try {
                        	// 重点
                            let x = onRejected(this.value)
                            resolvePromise(x, promise2, resolve, reject)
                        }catch(e) {
                            reject(e)
                        }
                    }, 0)
                })
            }
        })
        return promise2
    }
}
module.exports = Promise
  • 验证我们的promise是否符合PromiseA+规范
  1) 首先,在promise实现的代码中,增加以下代码:
    Promise.defer = Promise.deferred = function () {
        let dfd = {};
        dfd.promise = new Promise((resolve, reject) => {
            dfd.resolve = resolve;
            dfd.reject = reject;
        });
        return dfd;
    }

  2) 安装测试脚本:
    npm install -g promises-aplus-tests
    如果当前的promise源码的文件名为promise.js

  3) 那么在对应的目录执行以下命令: promises-aplus-tests promise.js
  • 执行 promises-aplus-tests promise.js 结果
...
The value is `1` with `Number.prototype` modified to have a `then` method
    ✓ already-fulfilled
    ✓ immediately-fulfilled
    ✓ eventually-fulfilled
    ✓ already-rejected
    ✓ immediately-rejected
    ✓ eventually-rejected


872 passing (16s)

到此为此,我们的promise完成了,且符合PromiseA+规范。

做几道练习题巩固巩固

  console.log(1);
  async function async () {
      console.log(2);
      await console.log(3); // Promise.resolve(console.log(3)).then(()=>{4})
      console.log(4)
  }
  setTimeout(() => {
      console.log(5);
  }, 0);
  const promise = new Promise((resolve, reject) => {
      console.log(6);
      resolve(7)
  })
  promise.then(res => {
      console.log(res)
  })
  async();
  console.log(8);
  Promise.resolve().then(()=>{ // then1
      console.log(1);
      Promise.resolve().then(()=>{ // then11
          console.log(11);
      }).then(()=>{
          console.log(22); // then22
      }).then(()=>{
          console.log(33); // then33
      })
  }).then(()=>{ // then2
      console.log(2);
  }).then(()=>{ // then3
      console.log(3);
  })
  async function async1(){ // node 11 结果不太一样
      console.log('async1 start');
      // node的其他版本可能会把await 解析出两个then 来
      await async2(); // Promise.resolve(async2()).then(()=>{console.log('ok')})
  }
  async function async2(){
      console.log('async2');
  }
  console.log('script start');
  setTimeout(() => {
      console.log('setTimeout')
  }, 0);
  async1();
  new Promise(function(resolve){
      console.log('promise1');
      resolve()
  }).then(function(){
      console.log('script end')
  })

promise的方法实现,包含Promise.resolve, Promise.reject, all, race, 取消promise,finally, catch, defer(延迟对象)

  • Promise.resolve实现
  Promise.resolve = function(val) {
      return new Promise((resolve, reject) => {
          resolve(val)
      })
  }
  • Promise.reject实现
  Promise.reject = function(val) {
      return new Promise((resolve, reject) => {
          reject(val)
      })
  }
  • Promise.all实现
  Promise.all = function(arr) {
      return new Promise((resolve, reject) => {
          let index = 0
          let values = []
          for (let i = 0; i<arr.length; i++) {
              Promise.resolve(arr[i]).then(r => {
                  values[i] = r
                  if(++index === arr.length){
                      resolve(values)
                  }
              }, y=>{
                  reject(y)
              })
          }
      })
  }
  // 用例
  Promise.all([1,2,3]).then(v=>{
      console.log(v)
  })
  • Promise.race实现
  Promise.race = function(arr) {
      return new Promise((resolve, reject) => {
          for(let i=0;i<arr.length;i++){
              Promise.resolve(arr[i]).then(r=>{
                  resolve(r)
              }, y=>{
                  reject(y)
              })
          }
      })
  }
  // 用例
  Promise.race([1,2,3]).then(r=>{
      console.log(r)
  })
  • 取消promise
  function wrap(promise) {
      let abort = null
      let abortPromise = new Promise((resolve, reject) => {
          abort = reject
      })
      promise = Promise.race([promise, abortPromise])
      promise.abort = abort
      return promise
  }
  // 用例
  let promise = new Promise((resolve, reject) => {
      setTimeout(() => {
          resolve(1)
      }, 2000);
  })
  let newPromise = wrap(promise)
  setTimeout(() => {
      newPromise.abort(12)
  }, 1000);
  newPromise.then(r => {
      console.log(r)
  }, y => {
      console.log(y)
  })
  • finally实现
如果最后是错的,那就取最后错的值,如果一开始是错的,取一开始错的值。如果都是对的,取第一次对的值
// resolve(x) && resolve(y) => then resolve(x)
// resolve(x) && reject(y) => then reject(y)
// reject(x) && resolve(y) => then reject(x)
// reject(x) && reject(y) => then reject(y)
  Promise.prototype.finally = function(cb) {
      return this.then(value=> {
          return Promise.resolve(cb()).then(()=>value, y=>y)
      }, reason=>{
          return Promise.resolve(cb()).then(()=>{throw reason})
      })
  }
  // 用例
  new Promise((resolve, reject) => {
      reject(1)
  }).finally(() => {
      return new Promise((resolve, reject) => {
          resolve(2)
      })
  }).then(r => {
      console.log('r: ', r)
  }, y => {
      console.log('y: ', y)
  })

  • catch实现
  Promise.prototype.catch = function(cb) {
     return this.then(null, cb)
  }
  // 用例
  new Promise((resolve, reject) => {
      reject(1)
  }).catch(() => {
      console.log('catch')
  })
  // 等价于 ->
  new Promise((resolve, reject) => {
      reject(1)
  }).then(null, () => {
      console.log('catch')
  })
  • 延迟对象
  Promise.defer = Promise.deferred = function () {
      let dfd = {};
      dfd.promise = new Promise((resolve, reject) => {
          dfd.resolve = resolve;
          dfd.reject = reject;
      });
      return dfd;
  }