[英]How to wait for a .forEach loop to finish its async request before calling method recursively [ReactJS]?
This program is written using ReactJS and REST API is used to get the data.该程序使用 ReactJS 和 REST 编写,API 用于获取数据。 The problem I am facing is that I am trying to traverse a tree of data and I need to make an async request for each node in the tree.
我面临的问题是我正在尝试遍历数据树,并且需要为树中的每个节点发出异步请求。
The tree looks something like this: example tree树看起来像这样:示例树
Currently, my code traverses the tree in breadth-first order, and the order looks like: 1->2->3->4->4.目前,我的代码以广度优先顺序遍历树,顺序如下:1->2->3->4->4。 I need it to traverse the tree in depth-first order, so that the order is 1->3->4->2->4.
我需要它以深度优先顺序遍历树,因此顺序为 1->3->4->2->4。 The goal is to make an API request for each node in depth-first order and push the results into an array.
目标是以深度优先顺序为每个节点发出 API 请求,并将结果推送到数组中。
The data we start off with is an array, [node1, node2] , and the result array should be [node1, node3, node4, node2, node4] .我们开始的数据是一个数组[node1, node2] ,结果数组应该是[node1, node3, node4, node2, node4] 。
The issue is that the async nature of the method allows the code to move to node 2 before we've even traversed and pushed the child data for node 1 into our resulting array.问题是该方法的异步特性允许代码在我们遍历节点 1 并将其推送到我们的结果数组之前移动到节点 2。 Before we've completely traversed all possible children for node1, it's already moving on to node2 and pushing that into our result array, and then going on to process it as well.
在我们完全遍历 node1 的所有可能子节点之前,它已经移动到 node2 并将其推送到我们的结果数组中,然后继续处理它。 Here is an example of what the code looks like currently:
这是当前代码的示例:
var resultArray = [];
getNodes(nodesArray) {
nodesArray.forEach(node => {
//Store nodes
resultArray.push(node)
//make a new request
const newRequest = node.id;
//our async request
getNodeData(newRequest)
.then((data) => {
var childNodes = data.nodes;
//Call method again for child nodes
this.getNodes(childNodes);
})
.catch((error) => {
console.log(error)
})
})
}
I have tried using Promises to await for the data to come in, but no luck in getting the.forEach loop to actually wait before moving onto the next node.我已经尝试使用 Promises 来等待数据进入,但没有运气让 .forEach 循环在移动到下一个节点之前实际等待。
You can make the function async
, then use await
with a for... of
loop.您可以将 function 设为
async
,然后将await
与for... of
循环一起使用。
async getNodes(nodesArray) {
for (const node of nodesArray) {
resultArray.push(node)
const newRequest = node.id;
try {
const res = await getNodeData(newRequest);
const childNodes = res.nodes;
await this.getNodes(childNodes);
} catch (error) {
console.log(error);
}
})
}
Since getNodes
makes asynchronous calls, its result will be a Promise, so you can mark the getNodes
method as async
.由于
getNodes
进行异步调用,其结果将是 Promise,因此您可以将getNodes
方法标记为async
。
IMO getNodes
should return the list of nodes rather than modifying some external array as a side-effect, so getNodes
can be modified to return a Promise that resolves to the list of nodes. IMO
getNodes
应该返回节点列表而不是修改某些外部数组作为副作用,因此可以修改getNodes
以返回解析为节点列表的 Promise。
With a for-loop in an async function, you can take advantage of await
for both your getNodeData
async function call, and the getNodes
recursive call.通过异步 function 中的 for 循环,您可以在
getNodeData
异步 function 调用和getNodes
递归调用中利用await
。
// returns a Promise that resolves to a list of nodes
async getNodes(nodesArray) {
const resultArray = [];
for (const node of nodesArray) {
// Store nodes
resultArray.push(node);
// make a new request
const newRequest = node.id;
//our async request
try {
const data = await getNodeData(newRequest);
// call method again for child nodes
resultsArray.concat(await this.getNodes(childNodes));
} catch (error) {
console.error(error);
}
}
return resultsArray;
}
Promise.all
will help here, turning an array of Promises into a Promise for an array of values. Promise.all
将在这里提供帮助,将 Promise 数组转换为 Promise 以获取值数组。 We can use this together with a recursive call for the children, combining the parent and children into a single array, and then flattening the result.我们可以将它与对子项的递归调用一起使用,将父项和子项组合成一个数组,然后将结果展平。 Something like this should do it:
这样的事情应该这样做:
const getNodes = (nodes) => Promise.all ( nodes.map (node => getNodeData (node).then (parent => getNodes (parent.nodes).then (children => [parent, ...children])) ) ).then (nodes => nodes.flat()) getNodes ([1, 2]).then (console.log).catch (err => console.log (`Error: ${err}`))
.as-console-wrapper {max-height: 100%;important: top: 0}
<script> /* dummy implementation of getNodeData */ const getNodeData = ((data = {1: {id: 1, x: 'foo', nodes: [3, 4]}, 2: {id: 2, x: 'bar', nodes: [4]}, 3: {id: 3, x: 'baz', nodes: []}, 4: {id: 4, x: 'qux', nodes: []}} ) => (id) => new Promise ((res, rej) => setTimeout(() => data[id]? res(JSON.parse(JSON.stringify(data[id]))): rej(`${id} not found`)), 100) )() </script>
getNodeData
is a dummy implementation, just meant to return a quick Promise for your data by id. getNodeData
是一个虚拟实现,只是为了通过 id 为您的数据返回一个快速的 Promise。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.