I have the following program (it should be executable as such) in which I have problems to handle a recursive call to a method that involves many asynchronous operations.
As a starting point in the program I have a test()
method. It triggers the first call to _add()
to which it passes the root
object (a tree of nested "elements").
The _add
method is responsible for adding the nested elements into the storage
(which I have represented here by a simple array). In order to do so, I have written the function so that it calls itself on all the children elements.
Storing an element is an asynchronous operation. Hence, I am using an array of promises to hold the "not yet available" value of all children elements.
As show by test()
, I do not manage to properly handle the synchronization here. All elements should have been inserted in the storage before the promise returned by the first call to add()
resolves. This it apparently not the case as the output of the program shows.
Of course, since the parentId of a child refers to the id of its parent, it's important for me that the storage operation happens in the same order at which the tree of elements is explored.
I am anticipating to have the exact same kind of problem when trying to retrieve elements from the storage
.
Any help would be greatly appreciated.
Thanks in advance.
(() => {
const storage = [];
const root = {
_type: "root",
title: "root element",
gravity: null,
children: [
{
_type: "space",
title: "my first space",
gravity: 0,
children: [
{
_type: "bundle",
title: null,
gravity: 0,
children: [
{
_type: "vis",
title: "my first vis",
gravity: 0,
visualization: {
url: "http://dummyvis.com/#8766"
},
children: null
},
{
_type: "feature",
title: "my feature 1",
gravity: 1,
feature: {
componentType: "CPT_FEATURE1"
},
children: []
},
{
_type: "feature",
title: "my feature 2",
gravity: 1,
feature: {
componentType: "CPT_FEATURE2"
},
children: []
}
]
}
]
}
]
};
const store = (element, parentId) => {
return new Promise( (resolve, reject) => {
storage.push({id:element.id, _type:element._type, gravity: element.gravity, parent:parentId, created_date:Date.now() });
setTimeout( () => resolve(element), 500);
});
}
const _add = (element, recurse, parentId) => {
console.log(element._type);
if(!element._type)
throw new Error("an element must declare a key '_type'");
parentId = parentId || null;
return new Promise( (resolve, reject) => {
const promises = [];
promises.push( new Promise( (resolve, reject) => {
store(element, parentId)
.then(element => {
resolve(element);
})
.catch(err => reject(err));
}));
Promise.all(promises)
.then( res => {
if(recurse && element.children.length > 0) {
element.children.forEach( child => {
_add(child, recurse, element.id);
});
}
resolve(element);
})
.catch( err => reject(err));
});
}
const test = () => {
_add(root, true)
.then( res => {
console.log("----------------------------------------------------------");
console.log("Test completed. Nothing should be printed after this line!");
console.log("------------------------------------------------------...-");
})
.catch( err => {
});
}
test();
})();
output:
decisionspace
----------------------------------------------------------
Test completed. Nothing should be printed after this line!
------------------------------------------------------...-
bundle
visualization
feature
feature