Search code examples
javascriptnode.jspromiserequesttimeout

How to force resolve a promise after certain duration in node.js?


I am trying to download a large number images from their url(s) and then creating a PDF file out of them in Node.js. I'm using the image-downloader module to download the images in a promise chain and then once all promises are resolved, using another module, images-to-pdf, to trigger the creation of the pdf.
The problem is that most of the promises get resolved immediately in around 5-6 seconds, but there are a few images that are taking quite a bit of time to download. I'd like to force trigger the PDF creation after a certain interval of waiting. Can that be done?
Here is the code

var promises = data.map(function(element){
    return download.image(element)
    .then( ({filename}) => {
        console.log("Saved to ",filename);
        return filename;
    })
});

Promise.all(promises).then(function (data){
    var fileName = url.slice(url.indexOf("files")+7, url.length-1).replace("/","_")+".pdf";
    data.forEach(
        (element) => {console.log(element);
    });
    imagesToPdf(data, fileName)
    .then(console.log(" PDF creation done."));
})
.catch ((error) => {
    console.error(error);
});

The data getting passed in data.map is a JSON object of the following nature -

[
  { 
    url:"https://path//to//image//001.jpg",
    dest:"local//path//to//image//001.jpg"
  },
  { 
    url:"https://path//to//image//002.jpg",
    dest:"local//path//to//image//002.jpg"
  },
  { 
    url:"https://path//to//image//003.jpg",
    dest:"local//path//to//image//003.jpg"
  },
  ...
}]

Solution

  • You can use Promise.race() to add a timeout to each images promise and if you're going to resolve it, then you need to resolve it with some sentinel value (I use null here in this example) that you can test for so you know to skip it when processing the results.

    function timeout(t, element) {
        return new Promise(resolve => {
            // resolve this promise with null
            setTimeout(resolve, t, element);
        });
    }
    
    var promises = data.map(function(element) {
        const maxTimeToWait = 6000;
        return Promise.race([download.image(element).then(({ filename }) => {
            console.log("Saved to ", filename);
            return filename;
        }), timeout(maxTimeToWait)]);
    });
    

    Then, in your code that processes the results from Promise.all(), you need to check for null and skip those because those are the ones that timed out.