使用时有什么问题吗 async
/ await
在一个 forEach
环我正在尝试循环浏览一系列文件和 await
关于每个文件的内容。
import fs from 'fs-promise'
async function printFiles () {
const files = await getFilePaths() // Assume this works fine
files.forEach(async (file) => {
const contents = await fs.readFile(file, 'utf8')
console.log(contents)
})
}
printFiles()
这段代码确实有效,但会不会出问题?有人告诉我你不能用 async
/ await
在像这样的高阶函数中,我只是想问一下这是否有什么问题。
19条答案
按热度按时间f0brbegy1#
今天我遇到了多种解决方案。在foreach循环中运行异步等待函数。通过构建 Package 器,我们可以实现这一点。
更详细地解释它是如何工作的
jgwigjjp2#
目前array.foreach prototype属性不支持异步操作,但我们可以创建自己的多边形填充以满足需要。
就这样!现在,在这些to操作之后定义的任何数组上都可以使用async foreach方法。
让我们测试一下。。。
我们可以对其他一些数组函数做同样的处理,比如map。。。
... 等等:)
需要注意的一些事项:
迭代器函数必须是异步函数或承诺
以前创建的任何数组
Array.prototype.<yourAsyncFunc> = <yourAsyncFunc>
将没有此功能可用4urapxun3#
使用task、futurize和可遍历列表,您只需
这是你如何设置的
构建所需代码的另一种方法是
或者更注重功能
然后从父函数
如果你真的想在编码上有更多的灵活性,你可以这样做(为了好玩,我正在使用建议的管道转发操作符)
ps-我没有在控制台上尝试此代码,可能有一些输入错误…”笔直的自由泳,从圆顶上跳下!”正如90年代的孩子们所说:-P
7vux5j2d4#
从循环中调用异步方法是不好的。这是因为每个循环迭代将被延迟,直到整个异步操作完成。这不是很有效。它还避免了并行化的优点
async
/await
.更好的解决方案是一次创建所有承诺,然后使用
Promise.all()
. 否则,在前一个操作完成之前,不会启动每个后续操作。因此,代码可以重构如下:;
eblbsuwk5#
一种简单的插入式解决方案,用于替换
forEach()
正在替换不工作的等待循环forEach
具有map
及加入Promise.all(
从头开始。例如:
await y.forEach(async (x) => {
到await Promise.all(y.map(async (x) => {
额外的)
最后需要的是。s71maibg6#
就像@bergi的React,但有一点不同。
Promise.all
如果一个人被拒绝,他会拒绝所有的承诺。所以,使用递归。
附言
readFilesQueue
不在printFiles
导致副作用*由console.log
,最好是模拟、测试和/或间谍,因此,使用返回内容的函数(sidenote)并不酷。因此,代码可以简单地设计为:三个独立的函数,它们是“纯的”**并且没有副作用,处理整个列表,并且可以很容易地修改以处理失败的情况。
未来编辑/当前状态
node支持顶级wait(它还没有插件,没有,可以通过harmony标志启用),它很酷,但没有解决一个问题(策略上我只在lts版本上工作)。如何获取文件?
使用构图。给定代码,让我感觉这是在一个模块内,所以,应该有一个函数来完成它。如果没有,您应该使用iife将角色代码 Package 到一个异步函数中,创建一个简单的模块来为您完成所有工作,或者您可以使用正确的方法,即组合。
请注意,变量的名称会因语义而更改。您传递一个函子(一个可由另一个函数调用的函数),并在包含应用程序初始逻辑块的内存上接收指针。
但是,如果不是模块,您需要导出逻辑?
将函数 Package 为异步函数。
或者改变变量的名称,不管怎样。。。
*
副作用指的是应用程序可能改变状态/行为或在应用程序中引入错误(如io)的任何协同效应。**
“pure”是用撇号表示的,因为它不是纯函数,代码可以聚合到纯版本,而没有控制台输出,只有数据操作。除此之外,纯粹地说,您需要使用处理副作用的单子,这些单子容易出错,并且将错误与应用程序分开处理。
pbwdgjma7#
然而,上述两种解决方案都能起作用,antonio的代码更少,下面是它如何帮助我从数据库中解析数据,从几个不同的子引用中解析数据,然后将它们全部推到一个数组中,并在完成所有操作后以承诺的方式解析数据:
b09cbbtk8#
只是补充了原来的答案
原始答案中的平行阅读语法有时令人困惑,难以阅读,也许我们可以用另一种方法来写
对于顺序操作,不仅仅是…of,正常for循环也可以工作
fjaof16o9#
一个重要的警告是:
await + for .. of
方法和步骤forEach + async
这种方式实际上有不同的效果。有
await
在一个真实的世界里for
循环将确保逐个执行所有异步调用。和forEach + async
way将同时发出所有承诺,这会更快,但有时会让人不知所措(如果您执行一些db查询或访问一些有音量限制的web服务,并且不希望一次发出100000个呼叫)。你也可以使用
reduce + promise
(不那么优雅)如果你不使用async/await
并希望确保一个接一个地读取文件。也可以创建foreachasync来提供帮助,但基本上对底层循环使用相同的方法。
xcitsw8810#
图片价值1000字-仅用于顺序方法
背景:我昨晚也有类似的情况。我使用异步函数作为foreach参数。结果是不可预测的。当我对我的代码进行了3次测试时,它运行了2次没有问题,失败了1次((奇怪的事情)
最后,我总算想起来了&做了一些便笺簿测试。
场景1-在foreach中使用async可以实现多大程度的非连续性
场景2-使用上述@bergi建议的for-of循环
如果你是像我这样的小老派,你可以简单地使用经典for循环,这也很有效:)
我希望这对某人有帮助,祝你好运,干杯!
zyfwsgd611#
当然,代码确实有效,但我非常确定它没有达到您期望的效果。它只是触发多个异步调用,但是
printFiles
函数在此之后立即返回。顺序阅读
如果要按顺序读取文件,则不能使用
forEach
的确用现代的for … of
而是循环,其中await
将按预期工作:并行阅读
如果要并行读取文件,则不能使用
forEach
的确每个async
回调函数调用确实会返回一个承诺,但您将它们扔掉,而不是等待它们。只用map
相反,你可以等待你将得到的一系列承诺Promise.all
:smtd7mpg12#
bergi的解决方案在以下情况下运行良好:
fs
是基于承诺的。你可以用bluebird
,fs-extra
或fs-promise
为了这个。但是,对于节点的本机
fs
馆藏资料如下:注:
require('fs')
强制将函数作为第三个参数,否则抛出错误:6qqygrtg13#
此解决方案还对内存进行了优化,因此您可以在10000个数据项和请求上运行它。这里的其他一些解决方案将使大型数据集上的服务器崩溃。
在typescript中:
如何使用?
1sbrub3j14#
在一个文件中弹出几个方法将以序列化顺序处理异步数据,并为您的代码提供更传统的风格,这是非常轻松的。例如:
现在,假设保存在“./myasync.js”中,您可以在相邻文件中执行类似于以下操作:
5sxhfpxr15#
除了@bergi的答案之外,我想提供第三种选择。这与@bergi的第二个示例非常相似,但不是等待每个示例
readFile
单独地,你创建了一系列的承诺,每一个都在最后等待。请注意,函数传递给
.map()
不需要async
自从fs.readFile
仍然返回承诺对象。所以promises
是promise对象的数组,可以发送到Promise.all()
.在@bergi的回答中,控制台可能会按照读取顺序记录文件内容。例如,如果一个非常小的文件在一个非常大的文件之前完成了读取,那么它将首先被记录,即使小文件位于数据库中的大文件之后
files
数组。但是,在我上面的方法中,可以保证控制台将按照与提供的数组相同的顺序记录文件。