我是月弦笙音,今天给大家分享  为什么使用 reduce() 按顺序解析 promise 有效 ,嘎嘎的😍,看下面

在不使用对象的情况下编写异步 js 很像闭着眼睛烤蛋糕。这是可以做到的,但它会很混乱,你最终可能会shao到自己。😂Promise

一、开题展示

我不会说这是必要的,但你明白了。真是太好了😂。不过,有时它需要一点帮助来解决一些独特的挑战,比如当你试图按顺序解决一堆承诺时,一个接一个。 例如,当你通过 AJAX 进行某种批处理时,这样的技巧非常方便。你希望服务器处理一堆事情,但不是一次处理所有事情,因此随着时间的推移,你将处理间隔开来。

排除有助于简化此任务的包(如 Caolan McMahon 的异步库),最常建议的顺序解析 promise 的解决方案是获取一个事物的集合,并将它们简化为单个值,如下所示:Array.prototype.reduce()

let result = [1,2,5].reduce((accumulator, item) => {
  return accumulator + item;
}, 0); // <-- Our initial value.

console.log(result); // 8

但是,当用于我们的某种目的时,设置看起来更像是这样:reduce()

let userIDs = [1,2,3];

userIDs.reduce( (previousPromise, nextID) => {
  return previousPromise.then(() => {
    return methodThatReturnsAPromise(nextID);
  });
}, Promise.resolve());

或者,以更现代化的格式:

let userIDs = [1,2,3];

userIDs.reduce( async (previousPromise, nextID) => {
  await previousPromise;
  return methodThatReturnsAPromise(nextID);
}, Promise.resolve());

这很简洁!但在很长一段时间里,我只是知道这个解决方案,并将那段代码复制到我的项目中,因为它“有效”。
所以!
这篇文章是我要说清楚两件事:😍

1. 为什么这种方法会奏效?

2. 为什么我们不能用其他方法来做同样的事情?Array

二、 为什么这种方法会奏效?

请记住,它的主要目的是将一堆东西“简化”为一个东西,它通过在循环运行时将结果存储来实现这一点。但这不一定是数字。循环可以返回它想要的任何内容(如 promise),并在每次迭代中通过回调回收该值。

值得注意的是,无论值是什么,循环本身都不会改变其行为,包括其执行速度。它只是在线程允许的范围内以最快的速度在集合中运行。reduce() accumulator

这很难理解,因为它可能违背了你认为在这个循环中发生的事情(至少,它对我来说是这样)。当我们使用它来按顺序解析 promise 时,reduce() 循环实际上根本没有减慢速度。 它是完全同步的,像往常一样尽可能快地做正常的事情。

看以下代码片段,并注意回调中返回的 promise 如何完全不阻碍循环的进度!😎

function methodThatReturnsAPromise(nextID) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {

      console.log(`Resolve! ${dayjs().format('hh:mm:ss')}`);

      resolve();
    }, 1000);
  });
}

[1,2,3].reduce( (accumulatorPromise, nextID) => {

  console.log(`Loop! ${dayjs().format('hh:mm:ss')}`);

  return accumulatorPromise.then(() => {
    return methodThatReturnsAPromise(nextID);
  });
}, Promise.resolve());

在我们的控制台中:

"Loop! 11:28:06"
"Loop! 11:28:06"
"Loop! 11:28:06"
"Resolve! 11:28:07"
"Resolve! 11:28:08"
"Resolve! 11:28:09"

promise 按照我们预期的顺序解析,但循环本身是快速、稳定和同步的。在查看了 的 MDN polyfill 之后,这是有道理的。循环一遍又一遍地触发,没有什么异步的,这就是引擎盖下发生的事情:reduce() while() callback()

while (k < len) {
  if (k in o) {
    value = callback(value, o[k], k, o);
  }
  k++;
}

真正的魔力就发生在这儿:

return previousPromise.then(() => {
  return methodThatReturnsAPromise(nextID)
});

每次触发回调时,我们都会返回一个 promise,该 promise 解析为另一个 promise。虽然不会等待任何解决方案发生,但它确实提供的优势是能够在每次运行后将某些内容传递回同一个回调,这是独有的功能。因此,我们能够构建一个承诺链,这些承诺可以解析为更多的承诺,使一切都变得美好和有序:reduce()

look!👀

new Promise( (resolve, reject) => {
  // Promise #1
  
  resolve();
}).then( (result) => { 
  // Promise #2
  
  return result;
}).then( (result) => { 
  // Promise #3
  
  return result;
}); // ... and so on!

所以!这些也应该揭示为什么我们不能在每次迭代中只返回一个单一的新承诺。由于循环是同步运行的,因此每个 promise 都会立即触发,而不是等待之前创建的 promise

[1,2,3].reduce( (previousPromise, nextID) => {

  console.log(`Loop! ${dayjs().format('hh:mm:ss')}`);
  
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(`Resolve! ${dayjs().format('hh:mm:ss')}`);
      resolve(nextID);
    }, 1000);
  });
}, Promise.resolve());

在我们的控制台中:

"Loop! 11:31:20"
"Loop! 11:31:20"
"Loop! 11:31:20"
"Resolve! 11:31:21"
"Resolve! 11:31:21"
"Resolve! 11:31:21"

是否可以等到所有处理完成后再做其他事情?是的。

同步性并不意味着你不能在每个项目都完全处理完毕后在进行下一步操作。看:reduce()

function methodThatReturnsAPromise(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(`Processing ${id}`);
      resolve(id);
    }, 1000);
  });
}

let result = [1,2,3].reduce( (accumulatorPromise, nextID) => {
  return accumulatorPromise.then(() => {
    return methodThatReturnsAPromise(nextID);
  });
}, Promise.resolve());

result.then(e => {
  console.log("Resolution is complete! Let's party.")
});

由于我们在回调中返回的只是一个链式 promise,因此当循环结束时,我们得到的就是:一个 promise。在那之后,我们可以随心所欲地处理它,即使在它已经走完了很久之后。reduce()

三、那为神马其他 Array 方法都不起作用?💢

请记住,在js的引擎下,我们不会等待回调完成,然后再进入下一个项目。它是完全同步的。所有这些其他方法也是如此:reduce()

但是reduce()很特别😎

我们发现,之所以reduce对我们有用,是因为我们能够将某些内容直接返回到我们相同的回调(即 promise),然后我们可以通过将它解析为另一个 promise 来构建它。

但是,对于所有这些其他方法,我们无法将参数传递给回调返回的回调。相反,这些回调参数中的每一个都是预先确定的,因此我们无法利用它们来实现顺序承诺解析之类的事情。

[1,2,3].map((item, [index, array]) => [value]);
[1,2,3].filter((item, [index, array]) => [boolean]);
[1,2,3].some((item, [index, array]) => [boolean]);
[1,2,3].every((item, [index, array]) => [boolean]);

四、 结语

ok!我想这个为什么使用 reduce() 按顺序解析 promise 有效我已经说的很明白了。

至少,我希望以上这些有助于阐明为什么唯有reduce有资格以这种方式处理承诺,并可能让你更好地了解常见方法在后台的运作方式。reduce() Array

感谢浏览本文,共同进步🤞,若有更好的建议,欢迎评论区讨论哈🌹。

如果觉得这篇干货很有用,可以加个关关🥰,点个赞赞🥰,后面我会继续分享更多干货!感谢支持!😍

本文参与了SegmentFault 思否写作挑战赛活动,欢迎正在阅读的你也加入。

月弦笙音
45 声望5 粉丝

前端开荒