I'm building up a hierarchy of scripts, where all the scripts at the same level can be run in parallel, but each level needs to complete before the next level runs.
I had this working when I had a fixed, known number of levels, by querying the document for the scripts at level 1, having a function that returns a promise when the arg passed loads, pushing that function onto an array, repeating for each level, and then creating a Promise.all chain for the resulting arrays:
function loadScript (script) {
return new Promise(function(resolve, reject) {
script.setAttribute("src", script.getAttribute("data-src"));
script.onload = function() {
resolve(script.src);
};
script.onerror = function() {
reject(script.src);
}
});
}
function loadScripts() {
const firstScripts = document.querySelectorAll( "script[data-type='lazy']"
+ "[data-order='1']" );
const secondScripts = document.querySelectorAll( "script[data-type='lazy']"
+ "[data-order='2']" );
let first = [];
let second = [];
firstScripts.forEach(function(element) {
first.push(loadScript(element));
});
secondScripts.forEach(function(element) {
second.push(loadScript(element));
});
Promise.all(first)
.then(Promise.all(second))
.then(function() {
console.log('all scripts loaded');
})
.catch(function(script) {
console.log(script + ' failed to load.');
});
}
I'm now trying to abstract it a bit, so it will work with an arbitrary, unknown depth. This seems like a fine place to use Array.map
; however, the problem is, it's actually executing the Promise.
So, what I'm looking for is a way to map the calls to loadScript
such that they can be called later:
function loadScripts() {
let maxDepth = 0;
let hierarchy = [];
while ( Boolean(document.querySelector( "script[data-type='lazy']"
+ "[data-order='" + maxDepth + "']" )) ) {
hierarchy[ maxDepth ] = [ ...document.querySelectorAll(
"script[data-type='lazy'][data-order='" + maxDepth + "']" ) ];
maxDepth++;
}
hierarchy.forEach((scripts, level) => {
// these loadScript calls should ... not be calls. They should be queued to
// run later when walkHierarchy is called
hierarchy[ level ] = scripts.map((element) => loadScript( element ) );
});
walkHierarchy = async function (arr) {
for (const level of arr) await Promise.all(level);
}
walkHierarchy( hierarchy )
.then(function() {
console.log('all scripts loaded');
})
.catch(function(script) {
console.log(script + ' failed to load.');
});
}
Alternatively, if there's a way to take the walkHierarchy
logic and include it in the hierarchy.forEach
, such that each level completed before moving on to the next, that would be fine, too.