I have a nested array of Person
objects.
Each Person
object has a mandatory name
. Each Person
can also optionally have a children
field that contains an array of other Person
objects (that also have a children field - so the "depth" of the family tree can essentially go on forever.)
If there is no children, the children field will just be an empty array [].
E.g.
const family_name = "The Numbers";
const family = [{
name: "1",
children: [],
},
{
name: "2",
children: [{
name: "2-1",
children: [{
name: "2-1-1",
children: [],
}, ],
},
{
name: "2-2",
children: [],
}
],
},
{
name: "3",
children: [{
name: "3-1",
children: [],
}, ],
},
]
I need to POST the "parent" before the "child". When I POST a Person
, I get its id
back in the response.data
. This id
needs to be used in the immediate child's POST as a parent_id
so that child will be associated to the parent.
The topmost Person
will need to have their parent_id
be the family_name
.
Each "level" needs to be POSTed asynchronously as my back-end needs to preserve the order. (Note: Calculating the order of the Person
on the client-side and passing that value to the back-end is not a solution as Person
is actually a MPTT model where the insertion order matters.)
E.g. 1
then 2
then 3
E.g. 2
then 2-1
then 2-2
.
However, the nested Person
s can be POSTed in sync. For example, once POSTing 2
returns with a 201 response, its "sibling" 3
and its "child" 2-1
can be POSTed at the same time.
How can I optimally POST all Person
s in a nested array so that the order is preserved? Please note that I am using axios.
Edit: Here is some pseudo-code:
function postPersons(persons, parent_id) {
// Async POST all objects in persons
// e.g. POST 1 then 2 then 3 to family_name
// For each successful POST, if person has children,
// async POST those children to that person
// e.g. Once POST to 2 resolves, POST 2-1 then 2-2 to 2
// e.g. Once POST to 3 resolves, POST 3-1 to 3
// Repeat for all successful POSTs
// e.g. Once POST to 2-1 resolves, POST 2-1-1 to 2-1
}
postPersons(family, family_name)
CodePudding user response:
Instead of using async
/await
for a sequential loop, I'd recommend to use a synchronous loop and accumulate two things separately:
- the promise for the child that is processed sequentially (see here or there)
- an array of promises for all the recursive calls, that will be
Promise.all
'd in the end
This ensures proper order as well as immediate propagation of errors.
So the code will look like
function postPersons(persons, parent_id) {
const results = [];
let chain = Promise.resolve();
for (const person of persons) {
chain = chain.then(() =>
postSinglePerson(person, parent_id)
);
results.push(chain.then(result =>
postPersons(person.children, result.id)
));
}
return Promise.all(results);
}
postPersons(family, family_name).then(() => console.log('Done'), console.error);