手写一个promise

简介: 本文主要通过例举promise的用法,来实现一个promis

网络异常,图片无法展示
|


状态初始化


promise用法


let p = new Promise((res, rej) => {
   // res('success')
   rej('err')
 })
 console.log(p1); 
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: "success"


promise初始化实现


首先,确定promise用一个类可以实现,promise初始化时,需要知道promise的状态,以及promise的返回值。

网络异常,图片无法展示
|

在promise中,存在PromiseState、PromiseResult两属性,分别存放promise的状态和结果。


用户在new Promise时,传入的是一个回调函数,在函数中使用resolve、rejected。


  • 在调用resolve时,将状态修改为fulfilled,并给结果赋值。
  • 在调用reject时,将状态修改为rejected,并给结果赋值。


初始化如下:


class MyPromise {
  constructor(cb) {
    this['[[PromiseState]]'] = 'pending'
    this['[[PromiseResult]]'] = undefined
    this.onResolved = [] // 成功回调集合 两个集合暂未使用
    this.onRejected = [] // 存放失败的回调集合
    cb(this.#resolve.bind(this), this.#reject.bind(this))  // 需要绑定this,否则获取不到
  }
  #resolve(val) {
    this['[[PromiseState]]'] = 'fulfilled'
    this['[[PromiseResult]]'] = val
  }
  #reject(val) {
    this['[[PromiseState]]'] = 'rejected'
    this['[[PromiseResult]]'] = val
  }
}


如果在cb调用不对promise内部私有属性绑定this,运行后就会报错:

网络异常,图片无法展示
|


this输出为undefined,此时获取不到this,所以需要在cb调用时,绑定this。


静态方法


resolve


Promise.resolve() 返回一个解析过的promise对象。


使用方法


console.log(Promise.resolve('success')); 
// Promise{
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: "success"
// }


实现


直接使用resolve静态方法,输出的结果等同于 new Promise((res, rej) => { res('success') })。因此,在调用resolve方法时,需要实例化一个Promise类,在内部调用res。


static resolve(val) {
    return new MyPromise((res, rej) => {
      res(val)
    })
  }


reject


Promise.reject() 返回一个拒绝的Promise对象。


使用方法


console.log(Promise.reject('err')); 
// Promise{
// [[PromiseState]]: "rejected"
// [[PromiseResult]]: "err"
// }


实现


直接使用resolve静态方法,输出的结果等同于 new Promise((res, rej) => { rej('err') })


static reject(val) {
    return new MyPromise((res, rej) => {
      rej(val)
    })
  }


allSettled


Promise.allSettled() 接收一个由已经执行完promise(状态为:fulfilled或rejected)的结果组成的对象数组。


allSettled用法


let p2 = new Promise((res, rej) => {
      setTimeout(() => {
        res("p1");
      }, 2000);
    })
    let p3 = new Promise((res, rej) => {
      setTimeout(() => {
        rej("p2 err");
      }, 1000);
    })
    Promise.allSettled([p2, p3]).then(res => {
      console.log(res); // [{status: "fulfilled", value: "p1"},{status: "rejected", reason: "p2 err"}]
    })


实现方法


allSettled接收的是一个对象数组,不管状态如何,结果输出的也是一个数组。在数组循环调用then时,需要考虑两种状态的返回结果,在不同的状态中,分别设置该项中的数据返回格式。例如:{status: "fulfilled", value: "p1"},成功中两个属性分别为status和value。


在实现时,需要设置一个变量,存储返回新结果的长度num。如果num等于接收数组lists长度,则表明已经全部修改统一格式,此时就需要执行下一步回调。


static allSettled(lists) {
    let resArr = new Array(lists.length)
    let num = 0
    return new MyPromise((resolve) => {
      lists.forEach((item, key) => {
        let obj = {}
        item.then(
          (res) => {
            obj['status'] = 'fulfilled'
            obj['value'] = res
            resArr[key] = obj
            num++
            if (num >= lists.length) {
              resolve(resArr)
            }
          },
          (err) => {
            obj['status'] = 'rejected'
            obj['reason'] = err
            resArr[key] = obj
            num++
            if (num >= lists.length) {
              resolve(resArr)
            }
          },
        )
      })
    })
  }


all


Promise.all 等待所有的都完成,如果all中数组有一个错误,则执行异常。


all用法


let p2 = new Promise((res, rej) => {
      setTimeout(() => {
        res("p1");
      }, 2000);
    })
    let p3 = new Promise((res, rej) => {
      setTimeout(() => {
        res("p2");
      }, 1000);
    })
    Promise.all([p2, p3])
    .then(res => {
      console.log(res);
    })
 // ["p1", "p2"]


all实现


和allSettled类似,只需要考虑成功的情况,但是还是需要判断所有的是否都已执行完成。


static all(lists) {
    let newArr = new Array(lists.length)
    let num = 0
    return new MyPromise((resolve) => {
      lists.forEach((item, key) => {
        item.then((res) => {
          newArr[key] = res
          num++
          if (num >= lists.length) {
            resolve(newArr)
          }
        })
      })
    })
  }


race


Promise.race()谁执行的时间短就执行谁,无论状态如何。


race用法


let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('success')
   }, 1000)
 })
let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('failed')
  }, 500)
})
Promise.race([p1, p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)  // 打开的是 'failed'
})


race实现


在遍历lists时,直接调用then。


static race(lists) {
    return new MyPromise((resolve, reject) => {
      lists.forEach((item) => {
        item.then(
          (res) => {
            resolve(res)
          },
          (err) => {
            reject(err)
          },
        )
      })
    })
  }


any


Promise.any()返回成功状态中的第一个。


any用法


let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('success')
    }, 1000)
  })
  let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('failed')
    }, 500)
  })
  Promise.any([p1, p2]).then((result) => {
    console.log(result)
  }).catch((error) => {
    console.log(error)
  })
 // success


any实现


执行成功状态的第一个回调。如果可迭代对象中没有一个 promise 成功(即所有的 promises 都失败/拒绝),就返回一个失败的 promise 和AggregateError类型的实例。


static any(lists) {
  return new MyPromise((resolve, reject) => {
    let errs = []
    let len = lists.length
    if (len === 0) reject(new AggregateError('All promises were rejected'))
    lists.forEach(item => {
      item.then(res => {
        resolve(res)
      }, err => {
        len--
        errs.push(err)
        if (len === 0) reject(new AggregateError(errs))
      })
    })
  })
}


原型方法


then原型方法


Promise.prototype.then() 最多接收两个回调函数:成功和失败情况下的回调函数。


基本then


基本then用法


let p = new Promise((res, rej) => {
      res('success');
    })
   p.then(res => {
        console.log(res); // success
    }, err => {
      console.log(err);
    })


基本then的实现


通过上段then的基本用法,可以根据new Promise内执行的res或rej,then中分别对应正确的回调或抛出错误。在then中就可以判断promise的状态来执行不同的回调。


then(onFullfill, onReject) {
    if (this['[[PromiseState]]'] === 'fulfilled') {
      onFullfill(this['[[PromiseResult]]'])
    }
    if (this['[[PromiseState]]'] === 'rejected') {
      onReject(this['[[PromiseResult]]'])
    }
}


状态为fulfilled时,表示成功;为rejected,表示失败。 调用多个then时,根据promise中调用的释放回调用,同时多个then中相应的回调也会触发。


let p = new MyPromise((res, rej) => {
    res('success')
  });
p.then(res => {
  console.log(res);
}, err => {
  console.log('err', err);
})
p.then((res) => {
 console.log(res, '2');
}, (rej) => {
  console.log('err2');
})
// success
// success 2


then的链式调用


then的链式用法


let p1 = new Promise((res, rej) => {
      res('success');
    })
    p1.then(res => {
      return new Promise((res, rej) => {
        res('success2')
      })
    }, err => {
      console.log(err);
    }).then(res => {
      console.log('2', res); // 2 success2
    })


then的链式实现


链式的实现,就相当于返回一个promise。根据上一个promise的状态,在then中执行相应状态的回调,依次类推。如果上一个then中返回一个promise,则此时then中的状态为'pending'。


#resolve(val) {
    if (this['[[PromiseState]]'] !== 'pending') return
    setTimeout(() => {
      this['[[PromiseState]]'] = 'fulfilled'
      this['[[PromiseResult]]'] = val
      // this.onResolvedQueue.forEach((fn) => fn && fn(val))
      let cb;
      while(cb = this.onResolvedQueue.shift()) {
        cb(val)
      }
    })
  }
  #reject(err) {
    if (this['[[PromiseState]]'] !== 'pending') return
    setTimeout(() => {
      this['[[PromiseState]]'] = 'rejected'
      this['[[PromiseResult]]'] = err
      // this.onRejectedQueue.forEach((fn) => fn && fn(err))
      let cb;
      while(cb = this.onRejectedQueue.shift()) {
        cb(err)
      }
    })
  }
 then(onFullfill, onReject) {
    // 返回的是新promise
    return new MyPromise((resolve, reject) => {
      // 成功执行的函数
      let onFullfillFn = (val) => {
        let res = onFullfill && onFullfill(val)
        // 返回一个新的promise 执行下一个回调
        res instanceof MyPromise ? res.then(resolve) : resolve(res)
      }
      // 失败执行的函数
      let rejectFn = (err) => {
        onReject && onReject(err)
        reject(err)
      }
      this.onResolvedQueue.push(onFullfillFn)
      this.onRejectedQueue.push(rejectFn)
    })
  }


then中会接受最多两个的回调函数,在成功的回调中,传入的可能直接是逻辑处理,也可能是需要返回一个新的promise对象,所以在返回时,需要判断返回类型。如果传入的是失败函数,直接执行用户失败的回调。


catch原型方法


Promise.prototype.catch() 捕获rejected情况,并处理。


catch用法


let p = new Promise((res, rej) => {
      rej('err')
})
p.catch((reason) => {
  console.log(reason) // err
})


catch实现


直接执行then的失败回调。


catch(cb) {
    this.then(undefined, cb)
  }


finally原型方法


Promise.prototype.finally() 无论是fulfiled还是rejected,都会执行finally内的回调函数。


finally用法


let p = new Promise((res, rej) => {
      res('success')
    })
    p.then((reason) => {
      console.log(reason)
    }).finally(() => {
      console.log('finally');
    })
// success finally


finally实现


finally(cb) {
    return this.then(cb, cb)
  }
目录
相关文章
|
6月前
|
前端开发 JavaScript
在JavaScript中,什么是promise、怎么使用promise、怎么手写promise
在JavaScript中,什么是promise、怎么使用promise、怎么手写promise
99 4
|
前端开发
手写基础 promise
手写基础 promise
66 0
|
存储 前端开发
|
前端开发
Promise的用法&原理&手写实现-2
Promise的用法&原理&手写实现-2
42 1
|
5月前
|
存储 前端开发 API
技术笔记:Promise的原理探究及手写Promise
技术笔记:Promise的原理探究及手写Promise
34 0
|
6月前
|
前端开发 JavaScript API
手写promise
手写promise
40 0
|
6月前
|
存储 前端开发 JavaScript
面试官:请手写一个Promise
面试官:请手写一个Promise
|
前端开发 JavaScript API
Promise的用法&原理&手写实现-1
Promise的用法&原理&手写实现-1
54 0
|
前端开发
手写Promise
手写Promise
|
前端开发 vr&ar
自定义手写Promise
手写函数版和类版Promise
57 0