手撸 Promise

手撸 Promise

  • Promise作为ES6的核心内容,是前端童鞋必备的基础知识!更是面试通关的必刷题!
  • Promise的出现,解决了"蛮荒时代"的回调地狱,让js异步callback走向简洁,优雅!
  • 本文参照Promise/A+实现(不足之处,请留言指出)

第一版(初出茅庐)

状态
  • Promise有三种状态值标识当前的异步状态!
  • pending状态可变为fulfilled 或 rejected
  • 一旦处于fulfilled 或 rejected时 则不可改为其他状态
const PENDING = 'pending' // 待定,等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

promise的基本实现

  • promise构造函数、resolve方法、reject方法、then方法
// promise构造函数
function Promise(executor) {
  this.state =PENDING
  this.value = undefined // 当状态fulfilled,给定一个value值
  this.reason = undefined // 当状态rejected,给定一个失败原因
  
  // resolve方法
  function resolve(value) {
    if (this.state === PENDING) {
      this.state = FULFILLED
      this.value = value
    }
  }
  
  
  // reject方法
  function reject(reason) {
    if (this.state === PENDING) {
      this.state = REJECTED
      this.reason = reason
    }
  }

  try {
    executor(resolve, reject)
  } catch (e) {
    reject(e)
  }
}
  • then方法

当状态为fulfilled时调用onFulfilled, 当状态为rejected时调用onRejected


Promise.prototype.then =  function(onFulfilled, onRejected) {
    if(this.state === FULFILLED){
        onFulfilled(this.value)
    }
    if(this.state === FULFILLED){
        onFulfilled(this.reason)
    }
}

此时基本有了promise的样子,但Promise真正要解决的异步问题,并未处理!

第二版 克敌(异步)制胜

  • 头脑(浆糊)风暴,思考then方法是同步调用,那必然会在异步任务完成前执行,该如何处理呢?

此时次刻,聪明的你必然想到了订阅发布,先通过then订阅异步成功后要执行的任务,在resolve 或 reject调用后执行已订阅的任务

// 给Promise中添加两个事件池,存储订阅事件

function Promise(executor) {
  ......
  this.onFulfilledCallBack = [] // onFulfilled订阅池
  this.onRejectedCallBack = [] // onRejected订阅池
  ......

// resolve方法、reject方法

  function resolve(value) {
    if (self.state === PENDING) {
      self.state = FULFILLED
      self.onFulfilledCallBack.forEach(cb => cb(self.reason))
    }
  }

  function reject(reason) {
    if (self.state === PENDING) {
      self.state = REJECTED
      self.reason = reason
      self.onRejectedCallBack.forEach(cb => cb(self.reason))
    }
  }

// 将onFulfilled, onRejected 放入事件池中

Promise.prototype.then =  function(onFulfilled, onRejected) {
    if(this.state === FULFILLED){
        onFulfilled(this.value)
    }
    if(this.state === FULFILLED){
        onFulfilled(this.reason)
    }
    if(this.state === PENDING){
        this.onFulfilledCallBack.push(onFulfilled)
        this.onRejectedCallBack.push(onRejected)
    }
}

此时此刻,恭喜你正式成为一名江湖(江湖)剑客,可以行侠仗义了

带三版 千秋万代一统江湖-then方法的链式调用

  • 根据Promise/A+规范,then方法返回一个新的promise
Promise.prototype.then = function(onFulfilled, onRejected) {
    let promise2 = new Promise((resolve, reject) => {
      if (this.state === FULFILLED) {
        let x = onFulfilled(this.value);
        resolvePromise(promise2, x, resolve, reject);
      };
      if (this.state === REJECTED) {
        let x = onRejected(this.reason);
        resolvePromise(promise2, x, resolve, reject);
      };
      if (this.state === PENDING) {
        this.onFulfilledCallBack.push(()=>{
          let x = onFulfilled(this.value);
          resolvePromise(promise2, x, resolve, reject);
        })
        this.onRejectedCallBack.push(()=>{
          let x = onRejected(this.reason);
          resolvePromise(promise2, x, resolve, reject);
        })
      }
    })
    return promise2
}
  • 注意 resolvePromise 方法,我在调用事件池里的方法后,为什么要resolvePromise方法将新的promise2与回调方法的返回值通过resolvePromise处理呢???
  • 思考:若事件池内的方法(then中用户传入的回调方法),返回的也是一个promise我们处理逻辑又将如何执行
  • 根据示例:观察原生Promise
let test = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log(1)
    resolve(1)
  }, 1000)
})
test
  .then(data => {
    console.log('data:', data)
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log(2)
        resolve(2)
      }, 2000)
    }).then(data => {
      console.log('data:', data)
      console.log(3)
      return 3
    })
  })
  .then(data => {
    console.log('data:', data)
    console.log(4)
  })

// 输出结果
1
data: 1
2
data: 2
3
data: 3
4
  • 据此我们可以发现外层的then回调会在内层的promise的then回调全部执行完才会执行,那内外层的promise必然有着不可告人的秘密,他们就是通过resolvePromise方法“勾结”在一起
resolvePromise方法
  • 参数有promise2(then默认返回的promise)、x(then回调中return的对象)、resolve、reject

// 这里先给出完整的 resolvePromise 代码,看完后我们来思考几个问题

function resolvePromise(promise2, x, resolve, reject){
  // 处理循环引用
  if(x === promise2){
    // reject报错
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  // 防止多次调用
  let called;
  if (x instanceof Promise) {
    if (x.state === PENDING) {
      x.then(
        y => {
          resolvePromise(promise2, y, resolve, reject)
        },
        reason => {
          reject(reason)
        }
      )
    } else {
    
      x.then(resolve, reject)
    }
  } else if (x != null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      // A+规定,声明then = x的then方法
      let then = x.then;
      // 如果then是函数,就默认是promise了
      if (typeof then === 'function') { 
        // 就让then执行 第一个参数是this   后面是成功的回调 和 失败的回调
        then.call(x, y => {
          // 成功和失败只能调用一个
          if (called) return;
          called = true;
          // resolve的结果依旧是promise 那就继续解析
          resolvePromise(promise2, y, resolve, reject);
        }, err => {
          // 成功和失败只能调用一个
          if (called) return;
          called = true;
          reject(err);// 失败了就失败了
        })
      } else { 
           // 返回非promise,内层promise完成,执行promise2的resolve 回归到外城then的调用
        resolve(x); // 直接成功即可
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e); 
    }
  } else {
    resolve(x);
  }
}
  • 思考1: x === promise2,看下例子
let p1 = new Promise(resolve => {
  resolve(1);
});
var p2 = p1.then(data => {
  return p2;
})

如果不对x === promise2处理,会导致循环引用,自己等待自己

  • 思考2: else if (x != null && (typeof x === 'object' || typeof x === 'function'))

兼容符合 Promise/A+ 的 then 方法, 使实现方式不同(只要符合规范)的promise能够相互调用。

第五版 内外兼修 处理异常

  • then中onFulfilled, onRejected均为非必填参数
  • onFulfilled, onRejected均为异步调用
  • onFulfilled, onRejected执行时用try catch捕获异常然后reject(err)
Promise.prototype.then =  function(onFulfilled, onRejected) {

    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    
    // onRejected 若未传,则通过throw err的方式将err抛出
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };

    let promise2 = new Promise((resolve, reject) => {
    
      if (this.state === FULFILLED) {
       setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0) 
      };
      
      if (this.state === REJECTED) {
           setTimeout(() => {
          // 如果报错
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      };
      
      if (this.state === PENDING) {
        this.onFulfilledCallBack.push(()=>{
             setTimeout(() => {
              try {
                let x = onFulfilled(this.value);
                resolvePromise(promise2, x, resolve, reject);
              } catch (e) {
                reject(e);
              }
            }, 0);
        })
        this.onRejectedCallBack.push(()=>{
             setTimeout(() => {
              try {
                let x = onRejected(this.reason);
                resolvePromise(promise2, x, resolve, reject);
              } catch (e) {
                reject(e);
              }
            }, 0);
        })
      }
    })
    
    return promise2
}
  • 到此你已完成了一个promise

完整代码


// state 3种状态
const PENDING = 'pending' // 待定
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

function Promise(executor) {
  this.state = PENDING
  this.value = undefined
  this.reason = undefined
  this.onFulfilledCallBack = []
  this.onRejectedCallBack = []
  let resolve = value => {
    if (this.state === PENDING) {
      this.state = FULFILLED
      this.value = value
      this.onFulfilledCallBack.forEach(fn => fn())
    }
  }
  let reject = reason => {
    if (this.state === PENDING) {
      this.state = REJECTED
      this.reason = reason
      this.onRejectedCallBack.forEach(fn => fn())
    }
  }
  try {
    executor(resolve, reject)
  } catch (err) {
    reject(err)
  }
}

Promise.prototype.then = function(onFulfilled, onRejected) {
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value

  // onRejected 若未传,则通过throw err的方式将err抛出
  onRejected =
    typeof onRejected === 'function'
      ? onRejected
      : err => {
          throw err
        }

  let promise2 = new Promise((resolve, reject) => {
    if (this.state === FULFILLED) {
      setTimeout(() => {
        try {
          let x = onFulfilled(this.value)
          resolvePromise(promise2, x, resolve, reject)
        } catch (e) {
          reject(e)
        }
      }, 0)
    }
    if (this.state === REJECTED) {
      setTimeout(() => {
        // 如果报错
        try {
          let x = onRejected(this.reason)
          resolvePromise(promise2, x, resolve, reject)
        } catch (e) {
          reject(e)
        }
      }, 0)
    }
    if (this.state === PENDING) {
      this.onFulfilledCallBack.push(() => {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      })
      this.onRejectedCallBack.push(() => {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      })
    }
  })

  return promise2
}

function resolvePromise(promise2, x, resolve, reject) {
  // 处理循环引用
  if (x === promise2) {
    // reject报错
    return reject(new TypeError('Chaining cycle detected for promise'))
  }
  // 防止多次调用
  let called
  if (x instanceof Promise) {
    if (x.status === PENDING) {
      x.then(
        y => {
          resolvePromise(promise2, y, resolve, reject)
        },
        reason => {
          reject(reason)
        }
      )
    } else {
      x.then(resolve, reject)
    }
  } else if (x != null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      // A+规定,声明then = x的then方法
      let then = x.then
      // 如果then是函数,就认为是promise
      if (typeof then === 'function') {
        // then执行,降this指向x
        then.call(
          x,
          y => {
            // 成功和失败只能调用一个
            if (called) return
            called = true
            // resolve的结果依旧是promise 那就继续解析
            resolvePromise(promise2, y, resolve, reject)
          },
          err => {
            // 成功和失败只能调用一个
            if (called) return
            called = true
            reject(err) // 失败了就失败了
          }
        )
      } else {
        // 返回非promise,内层promise完成,执行promise2的resolve 回归到外城then的调用
        resolve(x) // 直接成功即可
      }
    } catch (e) {
      if (called) return
      called = true
      reject(e)
    }
  } else {
    resolve(x)
  }
}

你可能感兴趣的:(javascript,es6)