Js手写简单的Promise、Promise.all、Promise.race

Js手写简单的Promise、Promise.all、Promise.race

最近准备面试,看八股文手撕Promise,想着也没没事,就看看怎么实现,不得不说现在前端真的卷~~ 全部基本都是转载一位大佬的,这里只简单的整理、实现了Promise、Promise.all、Promise.race,复杂的请移步大佬文章:原文链接,现在找个实习工作都这么难找吗,难道真的是人们说的前端已死???

Promise

  • 首先我们在调用 Promise 时,会返回一个 Promise 对象。
  • 构建 Promise 对象时,需要传入一个 executor 函数,Promise 的主要业务流程都在 executor 函数中执行。
  • 如果运行在 excutor 函数中的业务执行成功了,会调用 resolve 函数;如果执行失败了,则调用 reject 函数。
  • Promise 的状态不可逆,同时调用 resolve 函数和 reject 函数,默认会采取第一次调用的结果。

以上简单介绍了 Promise 的一些主要的使用方法,结合 Promise/A+ 规范,我们可以分析出 Promise 的基本特征:

  1. promise 有三个状态:pendingfulfilled,or rejected;「规范 Promise/A+ 2.1」
  2. new promise时, 需要传递一个executor()执行器,执行器立即执行;
  3. executor接受两个参数,分别是resolvereject
  4. promise 的默认状态是 pending
  5. promise 有一个value保存成功状态的值,可以是undefined/thenable/promise;「规范 Promise/A+ 1.3」
  6. promise 有一个reason保存失败状态的值;「规范 Promise/A+ 1.5」
  7. promise 只能从pendingrejected, 或者从pendingfulfilled,状态一旦确认,就不会再改变;
  8. promise 必须有一个then方法,then 接收两个参数,分别是 promise 成功的回调 onFulfilled, 和 promise 失败的回调 onRejected;「规范 Promise/A+ 2.2」
  9. 如果调用 then 时,promise 已经成功,则执行onFulfilled,参数是promisevalue
  10. 如果调用 then 时,promise 已经失败,那么执行onRejected, 参数是promisereason
  11. 如果 then 中抛出了异常,那么就会把这个异常作为参数,传递给下一个 then 的失败的回调onRejected

按照上面的特征,我们试着勾勒下 Promise 的形状:

class Promise {
  constructor (excutor) {
    // 规定状态
    this.state = 'peding'
    // 保存 `resolve(res)` 的res值
    this.value = undefined
    // 保存 `reject(err)` 的err值
    this.reason = undefined

    // 调用此方法就是成功
    let resolve = (value) => {
      // 状态为 PENDING 时才可以更新状态,防止 executor 中调用了两次 resovle/reject 方法
      if (this.state === 'peding') {
        this.state = 'fulfilled'
        this.value = value
      }
    }

    // 调用此方法就是失败
    let reject = (reason) => {
      // 状态为 PENDING 时才可以更新状态,防止 executor 中调用了两次 resovle/reject 方法
      if (this.state === 'peding') {
        this.state = 'rejected'
        this.reason = reason
      }
    }

    try {
      // 立即执行,将 resolve 和 reject 函数传给使用者  
      excutor(resolve, reject)
    } catch (error) {
      // 若出错,直接调用reject
      reject(error)
    }
  }

  // 包含一个 then 方法,并接收两个参数 onFulfilled、onRejected
  then (onFulfilled, onRejected) {
    if (this.state === 'fulfilled') {
      onFulfilled(this.value)
    }

    if (this.state === 'rejected') {
      onRejected(this.reason)
    }
  }
}

写完代码我们可以测试一下:

let p1 = new Promise((resolve, reject) => {
    resolve('ok1');
}).then(res => {
  console.log(res);
})

控制台输出:

ok1

现在我们已经实现了一个基础版的 Promise,但是还不要高兴的太早噢,这里我们只处理了同步操作的 promise。如果在 executor()中传入一个异步操作的话呢,我们试一下:

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok2')
  }, 1000);
}).then(res => {
  console.log(res);
})

执行测试脚本后发现,promise 没有任何返回。

因为 promise 调用 then 方法时,当前的 promise 并没有成功,一直处于 pending 状态。所以如果当调用 then 方法时,当前状态是 pending,我们需要先将成功和失败的回调分别存放起来,在executor()的异步任务被执行时,触发 resolve 或 reject,依次调用成功或失败的回调。

结合这个思路,我们优化一下代码:

class Promise {
  constructor (excutor) {
    // 规定状态
    this.state = 'peding'
    // 保存 `resolve(res)` 的res值
    this.value = undefined
    // 保存 `reject(err)` 的err值
    this.reason = undefined
    // 存放成功的回调
    this.successCB = []
    // 依次将对应的函数执行
    this.faildCB = []

    // 调用此方法就是成功
    let resolve = (value) => {
      // 状态为 PENDING 时才可以更新状态,防止 executor 中调用了两次 resovle/reject 方法
      if (this.state === 'peding') {
        this.state = 'fulfilled'
        this.value = value
        this.successCB.forEach(f => f())
      }
    }

    // 调用此方法就是失败
    let reject = (reason) => {
      // 状态为 PENDING 时才可以更新状态,防止 executor 中调用了两次 resovle/reject 方法
      if (this.state === 'peding') {
        this.state = 'rejected'
        this.reason = reason
        this.faildCB.forEach(f => f())
      }
    }

    try {
      // 立即执行,将 resolve 和 reject 函数传给使用者  
      excutor(resolve, reject)
    } catch (error) {
      // 若出错,直接调用reject
      reject(error)
    }
  }

  // 包含一个 then 方法,并接收两个参数 onFulfilled、onRejected
  then (onFulfilled, onRejected) {
    if (this.state === 'fulfilled') {
      onFulfilled(this.value)
    }

    if (this.state === 'rejected') {
      onRejected(this.reason)
    }

    if (this.state === 'peding') {
      this.successCB.push(() => { onFulfilled(this.value) })
      this.faildCB.push(() => { onRejected(this.reason) })
    }
  }
}

测试一下:

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok2')
  }, 1000);
}).then(res => {
  console.log(res);
})

控制台等待 1s 后输出:

ok2

ok!大功告成,异步问题已经解决了!

熟悉设计模式的同学,应该意识到了这其实是一个发布订阅模式,这种收集依赖 -> 触发通知 -> 取出依赖执行的方式,被广泛运用于发布订阅模式的实现。


Promise.all

promise.all 是解决并发问题的,多个异步并发获取最终的结果(如果有一个失败则失败)。

Promise.all = function (promises) {
  let list = []
  let count = 0
  function handle(i, data, resolve) {
    list[i] = data
    count++
    if (count == promises.length) {
      resolve(list)
    }
  }
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(res => {
        handle(i, res, resolve)
      }, err => reject(err))
    }
  })
}

测试一下:

let p1 = new Promise((resolve, reject) => {
    resolve('ok1');
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok2');
  }, 1000);
})

let p3 = new Promise((resolve, reject) => {
  resolve('ok3')
})

Promise.all([p1,p2,p3]).then(data => {
  console.log(data);
}, err => {
  console.log('reject', err);
})

控制台等待 1s 后输出:

['ok1', 'ok2', 'ok3']

测试一下返回失败:

let p1 = new Promise((resolve, reject) => {
    resolve('ok1');
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok2');
  }, 1000);
})

let p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('err');
  }, 1000);
})

Promise.all([p1,p2,p3]).then(data => {
  console.log(data);
}, err => {
  console.log('reject:', err);
})

控制台等待 1s 后输出:

reject: err

Promise.race

Promise.race 用来处理多个请求,采用最快的(谁先完成用谁的)。

Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(res =>  {
        resolve(res)
      }, err => {
        reject(err)
      })
    }
  })
}

测试一下:

let p1 = new Promise((resolve, reject) => {
    resolve('ok1');
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok2');
  }, 1000);
})

let p3 = new Promise((resolve, reject) => {
    resolve('ok3');
})

Promise.race([p1,p2,p3]).then(data => {
  console.log(data);
}, err => {
  console.log('reject', err);
})

控制台输出:

ok1

此文只实现简单的Promise,完整的请移步前面发的链接去查看,最后祝大家生活愉快,谢谢!!

@zwx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值