I am trying to deserialise a JSON object which has a type key whose value is a string; if the type is "a", the object class is defined in "a.js" and this file contains an initialisation function a_init. There may be a subobjects key whose value is an array of objects. If so, these nested objects should be processed in the same way.
The code I have looks like this:
var files = [];
function load(obj) {
if (!!obj["type"]) {
let type = obj.type;
let subobjects = obj.subobjects;
let promise = new Promise(function(resolve) {
let script = document.createElement("script");
script.setAttribute("src", type + ".js");
script.addEventListener("load",resolve);
document.head.appendChild(script);
});
promise.then(function() {
if (!files.includes(type)) {
files.push(type);
if (subobjects instanceof Array) {
for (element of subobjects) {
load(element);
}
}
let func = window[type + "_init"];
if (typeof(func) =="function") {
func(obj);
}
}
});
}
}
For an object like this:
{ "type": "a",
"data": "foo",
"subobjects": [
{ "type": "b",
"data": "1"
}
]
}
the output is:
This is A
This is B
(The functions a_init and b_init just display a message for testing purposes.)
My problem is that I would like the subobjects to be initialized before the outer object, so b_init would complete before a_init to give this output:
This is B
This is A
but of course the nested call to load just creates a new promise and then completes before the new promise does.
Is there any way to enforce synchronicity here to control the order of events?
You should convert your load function to async and inside it await for the promise and also for the recursive calls to finish.
var files = [];
// using an async function here so that we can
// use await inside it, but also so that we can
// await the whole function (it is recursively called
// and we want the recursions to fully complete before
// continuing)
async function load(obj) {
if (!!obj["type"]) {
let type = obj.type;
let subobjects = obj.subobjects;
const promise = new Promise(function(resolve) {
let script = document.createElement("script");
script.setAttribute("src", type + ".js");
script.addEventListener("load", resolve);
document.head.appendChild(script);
});
await promise; // wait for the script to be loaded
if (!files.includes(type)) {
files.push(type);
if (subobjects instanceof Array) {
for (element of subobjects) {
// await for the nested object to completely run
// before continuing. This will go deep first
await load(element);
}
}
let func = window[type + "_init"];
// we reach this place only after all nested objects
// completed.
if (typeof(func) == "function") {
func(obj);
}
}
}
}