手写promise

88 阅读4分钟

难点1:then的状态接受

1.如果promise内部回调没有执行resolve()或者reject()那么then将不执行,并分别将then中的两个回调行参then(()={},()=>{})存储数组中,但不会立即执行,并且promise的状态处于pending
2.如果promise内部回调执行了resolve()又执行了reject(),则当中先执行的会被执行,第二个不会被执行比如:
new Promise((resolve,reject)=>{
    resolve(1) // 执行,并将状态更改为 fulfilled
    reject(1)// 不会执行,因为一旦状态发生变化,将不会执行下
})
同时.then函数会根据相应的状态执行onResolve或onReject,对应的一个回调。
3.如果promise函数内部回调执行了reject,
而.then函数没有定义onReject,则此时会报错,并且promise状态为rejected;
而.then函数定义了onReject,则此时正常执行onReject,当时状态值为resolve;
4..then函数返回值是个新promise,则promise的状态该新promise状态
          返回值为非promise,则promise的状态为fulfilled

难点2:then的俄罗斯套娃

then函数套娃部分

if (result instanceof MyPromise) {
    result.then(nextResolve, nextReject);
} 
因为当前then返回的是一个promise,而promise并不能作为实际的resolve的value值,因此再执行一遍then,
此时传入then中的nextResolve回调,当中必定没有任何返回值,因此会走到
else {
      // 将上一个promise成功回调执行的结果传递给下一个promise成功的回调
      nextResolve(result);
    }
 nextResolve(result)相当于执行了resolve函数,resolve的value值变得正常

then函数的完整代码

then(onResolved, onRejected) {
    return new MyPromise((nextResolve, nextReject) => {
      // 1.判断有没有传入成功的回调
      if (this._isFunction(onResolved)) {
        // 2.判断当前的状态是否是成功状态
        if (this.status === FULFILLED) {
          try {
            // 拿到上一个promise成功回调执行的结果
            let result = onResolved(this.value);
            // console.log("result", result);
            // 判断执行的结果是否是一个promise对象
            if (result instanceof MyPromise) {
              result.then(nextResolve, nextReject);
            } else {
              // 将上一个promise成功回调执行的结果传递给下一个promise成功的回调
              nextResolve(result);
            }
          } catch (e) {
            nextReject(e);
          }
        }
      }
      // 1.判断有没有传入失败的回调
      // if(this._isFunction(onRejected)){
      try {
        // 2.判断当前的状态是否是失败状态
        if (this.status === REJECTED) {
          let result = onRejected(this.reason);
          if (result instanceof MyPromise) {
            result.then(nextResolve, nextReject);
          } else {
            nextResolve(result);
          }
        }
      } catch (e) {
        nextReject(e);
      }
      // }
      // 2.判断当前的状态是否是默认状态
      if (this.status === PENDING) {
        if (this._isFunction(onResolved)) {
          // this.onResolvedCallback = onResolved;
          this.onResolvedCallbacks.push(() => {
            try {
              let result = onResolved(this.value);
              if (result instanceof MyPromise) {
                result.then(nextResolve, nextReject);
              } else {
                nextResolve(result);
              }
            } catch (e) {
              nextReject(e);
            }
          });
        }
        // if(this._isFunction(onRejected)){
        // this.onRejectedCallback = onRejected;
        this.onRejectedCallbacks.push(() => {
          try {
            let result = onRejected(this.reason);
            if (result instanceof MyPromise) {
              result.then(nextResolve, nextReject);
            } else {
              nextResolve(result);
              nextReject();
            }
          } catch (e) {
            nextReject(e);
          }
        });
        // }
      }
    });
}

完整示例代码实现目标:

1.promise回调:

回调内部resolve和reject只会执行第一个,并且只改变一次状态;

回调内部可以写异步setTimeout函数,并延迟执行;

回调内部可以捕获throw错误,并通过reject传递;

2.then回调

若状态为 fulfilled,则执行成功回调;

若状态为 rejected,则执行失败回调;

3.then回调的返回结果

若回调返回一个新的prmoise,则then函数返回该的promise状态与新promise一致;

若回调放回一个非promise,则函数内部自动返回一个新的promise且状态为"fulfilled";

const FULLFILLED = 'fulfilled'

const REJECTED = 'rejected'

const PENDING = 'pending'

class MyPromise {

constructor(cb) {

this.status = PENDING

this.resolveCallbacks = []

this.rejectCallbacks = []

this.value = ''

this.reason = ''

// 用户直接在外部回调写throw,此处需要捕获

try {

cb(this._resolve.bind(this), this._reject.bind(this))

} catch(err) {

this._reject.call(this,err)

}

}

_resolve(value) {

if (this.status !== PENDING) return

this.status = FULLFILLED

this.value = value

// 需求是,then函数一定是在promise的回调执行完”之后“再执行。

/*

-要做到这点,需要如下:

1.then函数内判断状态为pending时候不要立即执行回调,而是将回调存储在相应的数组中

2._resolve函数执行状态变更完毕后,再去执行对应的then函数回调。

综上,能顺利满足需求

-例子:

若promise回调存在定时器,则定时器执行时候,在这之前then中的函数由于pending状态,早已手动加入数组中,

直到_resolve执行时候.then中的函数才会被执行

*/

//

this.resolveCallbacks.forEach(fn=>fn(this.value))

}

_reject(reason) {

if (this.status !== PENDING) return

this.status = REJECTED

this.reason = reason

// promise回调存在定时器的请

this.rejectCallbacks.forEach(fn=>fn(this.valureasone))

}

_isFunction(fn) {

if (typeof fn === 'function') {

return true

}

return false

}

then(onResolve, onReject) {

// 优化,不传递参数时 直接返回this

if (!this._isFunction(onResolve)) return this

return new MyPromise((nextResolve, nextReject) => {

// 不执行,只存储回调

if (this.status === PENDING) {

if (this._isFunction(onResolve)) {

this.resolveCallbacks.push(() => {

try {

// 保存回调结果

let result = onResolve(this.value)

if (result instanceof MyPromise) {

result.then(nextResolve, nextReject)

} else {

nextResolve(result)

console.log("执行了then中的回调")

\


}

} catch (err) {

nextReject(err)

}

})

} else if (this._isFunction(onReject)) {

this.rejectCallbacks.push(() => {

try {

// 保存回调结果

let result = onReject(this.reason)

if (result instanceof MyPromise) {

result.then(nextResolve, nextReject)

} else {

nextResolve(result)

}

} catch (err) {

nextReject(err)

}

})

} else {

nextResolve(this.value)

}

return

\


}

\


if (this.status === FULLFILLED) {

try {

// 保存回调结果

let result = onResolve(this.value)

if (result instanceof MyPromise) {

result.then(nextResolve, nextReject)

} else {

nextResolve(result)

}

} catch (err) {

nextReject(err)

}

}

\


// 特别注意:在then里面就是执行reject,this.status的状态也是fulfilled

if (this.status === REJECTED) {

if (this._isFunction(onReject)) {

try {

// 保存回调结果

let result = onReject(this.reason)

if (result instanceof MyPromise) {

result.then(nextResolve, nextReject)

} else {

nextResolve(result)

}

} catch (err) {

nextReject(err)

}

} else {

// nextReject(this.reason)

// setTimeout(()=>{

// throw new Error('Uncaught (in promise)' + this.reason)

// })

// return this

}

}

\


})

}

catch(onRejected) {

return this.then(undefined, onRejected);

}

}