4

I have a loop that gets iterated through 1000 times where each iteration makes a request and then prints the result of that request.

Similar to below.

let start = console.log(Date.now())
for (i = 0; i < 1000; i++) {
    request.then(data => {
        console.log(Date.now() - start)
    })
}

This however results in the first request taking much longer to be completed then if there was just 1 iteration of the loop.

with i < 1000:

5860
...

with i < 1:

220,
...

For the aim of this script however, I want to wait for each result to be received before starting the next iteration of the loop.

3
  • 1
    Are you sure you want to make the requests one after the other? Commented Oct 27, 2021 at 7:00
  • 1
    FYI: Its better to make i a let to prevent it from being created as a global variable. for(let i = 0; i < 1000; i++)... Commented Oct 27, 2021 at 7:06
  • @MauriceNino yes i have to unfortunately. My script is for a trading bot in which it needs to make as many requests as possible within 3 seconds. having it take 5s just for the first 1 to be returned makes it useless. Commented Oct 27, 2021 at 7:50

2 Answers 2

13

If you want to stick with the ES5 way of handling Promises, you could use the following approach:

const start = console.log(Date.now())

// Create a instantly resolved promise
for (let i = 0, p = Promise.resolve(); i < 1000; i++) {
  // append new promise to the chain
  p = p.then(() => request())
       .then(() => console.log(Date.now() - start));
}

If you can use ES6 methods, you could write this using the async/await pattern like so:

const asyncLoop = async () => {
  const start = console.log(Date.now())

  for (let i = 0; i < 1000; i++) {
    const data = await request();
    console.log(Date.now() - start);
  }
}

asyncLoop();

The chance that you really want to make the requests one after the other is actually pretty small though, so in case you want to make the request simultaneously, but do something after all of them resolved, you can use Promise.all(...)

const start = console.log(Date.now())
const requests = [request1, request2, ...];

Promise.all(requests)
       .then(() => console.log(Date.now() - start));
Sign up to request clarification or add additional context in comments.

5 Comments

Oh wow I never knew you could hack a for loop containing .then() by using p = Promise.resolve() to trick the execution stack! Anyway async/await is much cleaner
@JeremyThille Strongly agreed! async/await is the preferred way. If ES6 code is not allowed, I would strongly recommend to use tools like Typescript or Babel to compile ES6 code (easy to maintain) to ES5 code (runs in IE) instead of actually writing the ES5 code by hand.
Yeah, well, the "we must support IE" era is finally coming to an end, since Microsoft themselves have announced they are officially killing IE. So, ES6+ it is. If somebody asks me to develop an application that must run in IE10, I'll kindly ask them to seek another developer.
@JeremyThille IE will only die when the last person stops using it. As much as I hate it, some developers have to support it, because they work in the government or similar, where users are too old to make a change. For any other type of work, I am completely on your side, though.
Thankyou. I very much prefer using the ES6 method. its just what i had from when i was trying other methods. This is my first time using javascript so messing around. The script has to complete as many requests as possible within a 3 second period and so i have to do them sequentially.
-1

You can use the async-await pattern here. You can achieve this by changing the code

async function iteration() {
    let start = console.log(Date.now())
    for (let i = 0; i < 1000; i++) {
        const data = await httpRequest()
        console.log(Date.now() - start)
    }

}

async function httpRequest() {
    return new Promise((resolve, reject) => {
        request.then(data => {
            //Do anything you want to do with data
            resolve(data)
        }).catch(error => {
            console.error(`Error in request`)
            reject(error)
        })
    })
}

Explanation:

  1. I have moved the request code in a separate function httpRequest and this function will return promise on success
  2. I called httpRequest in for loop using await keyword, now loop will wait till the request is completed
  3. I have also wrapped for loop code in function since you can only use await inside an async function

3 Comments

request already returns a Promise, so you don't need to wrap it inside another Promise. Basically, your whole httpRequest() function could be rewritten... request. All you have to do is const data = await request(). And remove the whole useless httpRequest() function. Besides, why mix up together the old .then() Promise-like syntax, and the modern async/await one? Just get rid of .then() entirely.
Basically, 1. I have moved the request code in a separate function httpRequest and this function will return promise on success --> Why? :) You are just adding unnecessary code, complexity and old syntax by wrapping a Promise inside another Promise!
@JeremyThille You're correct. I just thought to add some extra logging or something. But yeah you have valid point.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.