Search code examples
javascriptasynchronouscoffeescriptpromisewinjs

Sequentially execute a bunch of WinJS promises from an array


I have an array of URLs that I want to download sequentially, 2 URLs at the time.

var urls = [url1,url2,url3,url4,url5];

The loop should download all the URLs using some async function, 2 urls at the time in the order they appear in the array. The basic sequential loop is done using .reduce that works like this:

preloadPromise = urls.reduce(function(p, url) {
  return p.then(WinJS.Utilities.Scheduler.schedulePromiseBelowNormal).then(function() {
    return preloadOneAsync(url);
  }).then(null, function(error) {
    if (error.name !== "Canceled") {
      logger.error("Could not create preloaded " + url, error);
    }
  });
}, WinJS.Promise.as());

Now I want to introduce parallel downloads of 2 URLs at the same time.

So it would download: [url1,url2] then [url3,url4] and finally [url5]

Also the result of the function should be the results of every download promise, similar to how WinJS.Promise.join works.


Solution

  • Let's abstract out the function to break the array into tuples

    function chunkBy(array, n) {
        var chunks = [];
        for (var i=0; i<array.length; i+=n)
            chunks.push(array.slice(i, n));
        return chunks;
    }
    

    and the function that does the work for each item:

    function tryToLoad(url) {
        return WinJS.Utilities.Scheduler.schedulePromiseBelowNormal() // not sure
        .then(function() {  return preloadOneAsync(url); })
        .then(null, function(error) {
            if (error.name !== "Canceled") {
                logger.error("Could not create preloaded " + url, error);
            }
        });
    }
    

    A function that does the work for multiple items in parallel would be just

    function loadAll(urls) {
        return WinJS.Promise.join(urls.map(tryToLoad));
    }
    

    so we can now use this in the generation of the sequence:

    preloadPromise = chunkBy(urls, 2).reduce(function(p, urls) {
        return p.then(function(prevResults) {
            return loadAll(urls).then(function(results) {
                return prevResults.concat(results);
            });
        });
    }, WinJS.Promise.as([]));
    

    The array of results is passed through the p chain, always growing after each step.