前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >手写源码系列(二)——Promise相关方法

手写源码系列(二)——Promise相关方法

作者头像
用户1687375
发布2019-07-30 16:47:20
9131
发布2019-07-30 16:47:20
举报

本文首发于知乎专栏——前端面试题汇总,大家可以通过文章底部的阅读原来来访问原文地址

手写Promise相关方法

Promise是面试中经常遇到的,如果面试中面试官问你Promise.all()怎么用,那你面试的岗位可能是差不多高级前端开发的岗位,但如果让你手写一个Promise.all()那你面试的岗位应该就是资深/专家前端开发的岗位了

上期回顾

上期我们实现了函数的call()bind()apply()方法。

Promise.all()

先回顾一下Promise.all()的用法

Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。

例子如下:

var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values);
});
// expected output: Array [3, 42, "foo"]
手写实现Promise.all()方法

直接上代码:

Promise.myAll = function (iterators) {
  const promises = Array.from(iterators)
  const len = promises.length
  let count = 0
  let resultList = []
  return new Promise((resolve, reject) => {
    promises.forEach((p, index) => {
      Promise.resolve(p)
        .then((result) => {
          count++
          resultList[index] = result
          if (count === len) {
            resolve(resultList)
          }
        })
        .catch(e => {
          reject(e)
        })
    })
  })
}

核心思路:

  1. Promise.myAll()返回的肯定是一个promise对象,所以可以直接写一个return new Promise((resolve, reject) => {})(这应该是一个惯性思维)
  2. 遍历传入的参数,用Promise.resolve()将参数"包一层",使其变成一个promise对象
  3. 关键点是何时"决议",也就是何时resolve出来,在这里做了计数器(count),每个内部promise对象决议后就将计数器加一,并判断加一后的大小是否与传入对象的数量相等,如果相等则调用resolve(),如果任何一个promise对象失败,则调用reject()方法。

一些细节:

  1. 官方规定Promise.all()接受的参数是一个可遍历的参数,所以未必一定是一个数组,所以用Array.from()转化一下
  2. 使用for…of进行遍历,因为凡是可遍历的变量应该都是部署了iterator方法,所以用for…of遍历最安全

Promise.race()

回顾一下race的用法
var promise1 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 500, 'one');
});

var promise2 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 100, 'two');
});

Promise.race([promise1, promise2]).then(function(value) {
  console.log(value);
  // Both resolve, but promise2 is faster
});
// expected output: "two"

有了Promise.all()的铺垫,race就好写多了。

Promise.myRace = function (iterators) {
    return new Promise((resolve, reject) => {
        for (let p of iterators) {
            Promise.resolve(p)
                .then((result) => {
                    resolve(result)
                })
                .catch(e => {
                    reject(e)
                })
        }
    })
}

核心思路:

  1. 谁先决议那么就返回谁,所以将all的计数器和逻辑判断全部去除掉就可以了。

Promise.prototype.finally()

回顾一下正常用法。

finally() 方法返回一个Promise。在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。这为在Promise是否成功完成后都需要执行的代码提供了一种方式。

这避免了同样的语句需要在then()catch()中各写一次的情况。

let isLoading = true;

fetch(myRequest).then(function(response) {
    var contentType = response.headers.get("content-type");
    if(contentType && contentType.includes("application/json")) {
      return response.json();
    }
    throw new TypeError("Oops, we haven't got JSON!");
  })
  .then(function(json) { /* process your JSON further */ })
  .catch(function(error) { console.log(error); })
  .finally(function() { isLoading = false; });
手写实现
Promise.prototype.myFinally = function finallyPolyfill(callback) {
    return this.then(function(value) {
            return Promise.resolve(callback()).then(function() {
                return value;
            });
        }, function(reason) {
            return Promise.resolve(callback()).then(function() {
                Promise.reject(reason);
            });
        });
};

核心思路:

  1. 当前this指向的是当前Promise对象,所以可以直接用this.then()
  2. then回调中的两个参数,一个是成功时的回调,另一个是失败是的回调,利用这个两个参数处理当前promise对象的两种不同情况
  3. 无论如何都要调用传入的callback函数,并且将当前promise的决议值继续传递下去

一些细节:

callback传入的有可能仍然是一个Promsie对象,如果真的是Promise对象,要等该promise决议之后才能执行之后then()方法,但是这个then()中拿到的是finally()之前的决议值,有种"决议值穿透"的感觉。

PS:我在网上找到了最权威的写法,毫无破绽 https://github.com/matthew-andrews/Promise.prototype.finally/blob/master/finally.js

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 较真的前端 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 手写Promise相关方法
  • 上期回顾
  • Promise.all()
    • 先回顾一下Promise.all()的用法
      • 手写实现Promise.all()方法
      • Promise.race()
        • 回顾一下race的用法
        • Promise.prototype.finally()
          • 回顾一下正常用法。
            • 手写实现
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档