JsEveryDay
JsEveryDay

Reputation: 323

How to add delay to a Promise resolve? Add delay to node http.request promise

Trying to add a small delay to each http.request in NodeJS but of course node always goes in sync mode. So the question is how do I reset the state of a promise once it's been fulfilled?

  1. Currently helper.delay() works only once in the beginning then all http requests are sent instantly, causing the server to error out.
  2. Why am I not able to use this.delay() on other promises inside my module.exports? But it works fine inside normal exportable functions.
  3. Off Topic. How to switch http to https depending on URL requested? (without copying the same code, eliminating redundancy).

Tried the default set timeout inside the promise, wrapped http.request with a new setTimeout promise, tried using an external "requestHandler" as a callback, which was running the function as a thennable. etc.etc Perhaps I have to clearTimeout? https://nodejs.org/api/timers.html

exports

const wait = require("util").promisify(setTimeout);
module.exports = {
    delay: async function(seconds){
        await wait(seconds * 1000);
    },
    getUrl: function(url){
        return new Promise((resolve, reject) => {
            const options = new URL(url);
            const resolver = http.request(options, res => {
                res.setEncoding("utf8");
                let responseBody = '';
                res.on("data", body => {
                    responseBody += body
                });
                res.on('end', () => resolve(responseBody));                
            });
            resolver.end();
        });
    }
};

app

const helper = require("./exports.js");
async function getdata(url) {
    const data = await help.getUrl(url);
    let results = JSON.parse(data);
    results = results.answer;
    return results;
}
function fetchData(el, i) {
    getdata(el).then(
        answer => { console.log(answer)},
        //...etc
        )
}
function getLinksFromFile(file) {
    helper.readFile(file)
        .then(arr => {
            arr.forEach((el, i) => {
            /////////ISSUE HERE BELOW//////////////
                helper.delay(4).then(() => {
                ////It only waits once
                    return fetchData(el, i);
                });
            });
        });
}
getLinksFromFile("./links.txt");

The objective is as simple as it can get, read urls form file, get json data from the url's and do stuff. Dependency Free

Upvotes: 0

Views: 1106

Answers (1)

t.niese
t.niese

Reputation: 40842

You need to do some more reading about how promises work or do some more experiments with Promises, to understand them better.

So what does this code do:

 arr.forEach((el, i) => {
            /////////ISSUE HERE BELOW//////////////
                helper.delay(4).then(() => {
                ////It only waits once
                    return fetchData(el, i);
                });
            });

You iterate over each element of arr and for each of them you create a Promise for the delay using helper.delay(4). But all of those Promises are created at roughly the same time (just some nanoseconds apart), because the forEach does not wait for the one Promise chain to be finished. So each fetchData is delay by 4second form the time when forEach happened.

The easiest way to solve that problem is to convert your code to use await/async syntax:

async function getLinksFromFile(file) {
    let arr = await helper.readFile(file);
    for( let i=0, i<arr.length ; i++) {
        let el = arr[i];
        await helper.delay(4)
        await fetchData(el, i)
    }
}

Using await in a regular loop will pause the loop at that point until the Promise is resolved.

Upvotes: 1

Related Questions