手写 Promise 所有方法实现

前言

上次我们手写了一个promise,相信大家已经对 Promise 有了深度的理解,这次我们再把 Promise 的所有相关方法都实现一下。

Promise.prototype.catch


Promise.prototype.catch = function(onRejected) {

  return this.then(null, onRejected)

}

catch 方法相对简单,就是调用 promise 自身的 then 方法,只传入失败的回调。因为 then 方法本身具有值的传递性,所以我们只需传入失败的回调即可,即使没有成功的回调,也会将值自动的传递下去。

Promise.prototype.finally

无论成功还是失败都会执行 finally


Promise.prototype.finally = function(onFinished) {

  return this.then(val => {

    onFinished()

    return val

  }).catch((err) => {

    onFinished()

    return err

  })

}

finally 需要注意

  1. finally 的回调没有参数

  2. promise 如果成功,则将成功的值正常传递下去,不会因 finally 而断掉,举例:

  3. promise 如果失败,同上


Promise

  .resolve(1)

  .finally(val => console.log(val)) // undefind

  .then(val => console.log(val)) // 1

Promise

  .reject('error')

  .finally(val => console.log(val)) // undefind

  .catch(err => console.log(err)) // error

Promise.resolve

返回一个成功的 promise


Promise.resolve = function(val) {

  return new Promise(resolve => {

    resolve(val)

  })

}

emmm 一眼就懂

image

Promise.reject

返回一个失败的 promise


Promise.reject = function(val) {

  return new Promise((resolve, reject) => {

    reject(val)

  })

}

啥也不说了。。。

image

Promise.all

返回一个 promise 实例。接受一个可迭代的对象,会等 iterable 参数内所有的 promise 都 resolved 时被 reslove,如果有一个 rejected,则此实例被 reject。

如果 iterable 没有 promise,则此实例直接被 resolve。

返回值是一个数组,数组的值按照 iterable 的迭代顺序排列。


Promise.all = function(ps) {

  let resolve

  let reject

  const promise = new Promise((r, j) => {

    resolve = r

    reject = j

  })

  let fufilledCount = 0

  let index = 0;

  const ret = [];

  const wrapFufilled = i => {

    return val => {

      fufilledCount += 1

      ret[i] = val

      if (fufilledCount >= index) {

        resolve(ret)

      }

    } 

  }

  const wrapRejected = i => {

    return err => {

      reject(err)

    }

  }

  for (let p of ps) {

    Promise.resolve(p).then(wrapFufilled(index), wrapRejected(index))

    index += 1

  }

  return promise

}

注意几个要点

  1. 因为接受的是一个可迭代对象(iterable),所以我们用 for of 遍历。

  2. 这里我们用 Promise.resolve() 包装一下所有的 promise。

  3. wrapFufilled 的目的是为了记录当前遍历的 index。

Promise.race

返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。


Promise.race = function(ps) {

  let resolve

  let reject

  const promise = new Promise((r, j) => {

    resolve = r

    reject = j

  })

  for (let p of ps) {

    Promise.resolve(p).then(

      val => resolve(val),

      err => reject(err)

    )

  }

  return promise

}

Promise.any

只要其中的一个 promise 成功,就返回那个已经成功的 promise 。如果可迭代对象中没有一个 promise 成功(即所有的 promises 都失败/拒绝),就返回一个失败的 promise 和AggregateError类型的实例,它是 Error 的一个子类,用于把单一的错误集合在一起。

本质上,这个方法和Promise.all()是相反的


Promise.any = function(ps) {

  let resolve

  let reject

  const promise = new Promise((r, j) => {

    resolve = r

    reject = j

  })

  let errCount = 0

  let pCount = 0

  for (let p of ps) {

    pCount += 1

    Promise.resolve(p).then(

      val => resolve(val),

      err => {

        errCount += 1

        if (errCount >= pCount) {

          reject(new AggregateError('All promises were rejected'))

        }

      }

    )

  }

  return promise

}

Promise.allSettled

返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。

这里举一个例子:


const promise1 = Promise.resolve(3);

const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));

const promises = [promise1, promise2];

Promise.allSettled(promises).

  then((results) => results.forEach((result) => console.log(result)));



// 输出结果

// Object { status: "fulfilled", value: 3 }

// Object { status: "rejected", reason: "foo" }

具体实现:


Promise.allSettled = function(ps) {

  let resolve

  let reject

  const promise = new Promise((r, j) => {

    resolve = r

    reject = j

  })

  let finishedCount = 0

  let index = 0;

  const ret = [];

  const wrapFufilled = i => {

    return val => {

      finishedCount += 1

      ret[i] = {

        status: 'fufilled',

        value: val

      }

      if (finishedCount >= index) {

        resolve(ret)

      }

    } 

  }

  const wrapRejected = i => {

    return err => {

      finishedCount += 1

      ret[i] = {

        status: 'rejected',

        value: err

      }

      if (finishedCount >= index) {

        resolve(ret)

      }

    }

  }

  for (let p of ps) {

    Promise.resolve(p).then(wrapFufilled(index), wrapRejected(index))

    index += 1

  }

  return promise

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349