首页 > 解决方案 > 通过使用 async/await 模拟 .reduce() 来循环异步调用

问题描述

代码中使用的变量和函数:

const fakeAPI = (delay, value) =>
 new Promise(resolve => setTimeout(() => resolve(value), delay));

const useResult = x => console.log(new Date(), x);

此代码通过使用 reduce() 实现来打印 forEach 不能使用异步调用的内容:

const forEachAsync = (arr, fn) =>
 arr.reduce(
 (promise, value) => promise.then(() => fn(value)),
 Promise.resolve()
 );
(async () => {
 console.log("START FOREACH VIA REDUCE");
 await forEachAsync([1, 2, 3, 4], async n => {
 const x = await fakeAPI(n * 1000, n);
 useResult(x);
 });
 console.log("END FOREACH VIA REDUCE");
})();

输出

/*
START FOREACH VIA REDUCE
2019-10-13T20:02:23.437Z 1
2019-10-13T20:02:24.446Z 2
2019-10-13T20:02:25.949Z 3
2019-10-13T20:02:27.952Z 4
END FOREACH VIA REDUCE
*/

arr.reduce在主代码块的上下文中,函数中的 promise 和 value 参数的目的是什么?

是否将value参数传递给async n箭头函数?

尽管如此,我更喜欢解释,因为我正在研究功能程序,但是,如果有任何其他方式以 FP 方式处理这个问题,我将不胜感激。

标签: javascriptasync-awaitfunctional-programmingemulationreduce

解决方案


为每个

forEach是您正在学习的代码不好的副作用。该示例使用asyncand awaitso.reduce来链接.then调用是没有意义的。也就是说,如果您想体验它的使用痛苦,我们可以实现它 -

async function forEach (arr, fn)
{ for (const x of arr)
    await fn(x)         // <- return value of fn disappears
}

const sleep = ms =>
  new Promise(r => setTimeout(r, ms))

const fakeApi = x =>
  sleep(1000).then(_ => `api response: ${x * x}`)

forEach
  ( [1,2,3,4]
  , async x =>
      // side effect tangled with task processing code
      console.log((new Date).toUTCString(), await fakeApi(x))
  )
  .then(console.log, console.error) // <- no values beyond this point

"Sat, 30 Jan 2021 16:46:35 GMT" "api response: 1"   // <- :35
"Sat, 30 Jan 2021 16:46:36 GMT" "api response: 4"   // <- :36
"Sat, 30 Jan 2021 16:46:37 GMT" "api response: 9"   // <- :37
"Sat, 30 Jan 2021 16:46:38 GMT" "api response: 16"  // <- :38
undefined

要解决上述问题,请注意如何serialparallel(下文)将数据包含console.logPromise 中,而不是使用诸如or之类的副作用推出值useResult

串行

我们可以按顺序编写哪些serial进程任务-

async function serial (arr, fn)
{ let r = []
  for (const x of arr)
    r.push(await fn(x))
  return r
}

const sleep = ms =>
  new Promise(r => setTimeout(r, ms))

const fakeApi = x =>
  sleep(1000).then(_ => `api response: ${x * x}`)

serial
  ( [1,2,3,4]
  , async x => [ (new Date).toUTCString(), await fakeApi(x) ]
  )
  .then(console.log, console.error)

[
  [
    "Sat, 30 Jan 2021 16:39:04 GMT",  // <- :04
    "api response: 1"
  ],
  [
    "Sat, 30 Jan 2021 16:39:05 GMT",  // <- :05
    "api response: 4"
  ],
  [
    "Sat, 30 Jan 2021 16:39:06 GMT",  // <- :06
    "api response: 9"
  ],
  [
    "Sat, 30 Jan 2021 16:39:07 GMT",  // <- :07
    "api response: 16"
  ]
]

平行

或者我们可以编写parallel哪些并行处理任务-

const parallel = (arr, fn) =>
  Promise.all(arr.map(v => fn(v)))

const sleep = ms =>
  new Promise(r => setTimeout(r, ms))

const fakeApi = x =>
  sleep(1000).then(_ => `api response: ${x * x}`)

parallel
  ( [1,2,3,4]
  , async x => [ (new Date).toUTCString(), await fakeApi(x) ]
  )
  .then(console.log, console.error)

[
  [
    "Sat, 30 Jan 2021 16:40:47 GMT",  // <- :47
    "api response: 1"
  ],
  [
    "Sat, 30 Jan 2021 16:40:47 GMT",  // <- :47
    "api response: 4"
  ],
  [
    "Sat, 30 Jan 2021 16:40:47 GMT",  // <- :47
    "api response: 9"
  ],
  [
    "Sat, 30 Jan 2021 16:40:47 GMT",  // <- :47
    "api response: 16"
  ]
]

推荐阅读