1

For the following function, I have to add a timeout after every GET request in array ajaxUrls. All the XHR GET request are in array ajaxUrls.

function getAllSearchResultProfiles(searchAjaxUrl) {
  var ajaxUrls = [];
  for (var i = 0; i < numResults; i += resultsPerPage) {
    ajaxUrls.push(searchAjaxUrl + "&start=" + i);
  }
  return Promise.all(ajaxUrls.map(getSearchResultsForOnePage))
    .then(function(responses) {
      return responses.map(function(response) {
        if (response.meta.total === 0) {
          return [];
        }
        return response.result.searchResults.map(function(searchResult) {
          return (searchResult);
        });
      });
    })
    .then(function(searchProfiles) {
      return [].concat.apply([], searchProfiles);
    })
    .catch(function(responses) {
      console.error('error ', responses);
    });
}

function getSearchResultsForOnePage(url) {
  return fetch(url, {
      credentials: 'include'
    })
    .then(function(response) {
      return response.json();
    });
}

I want a certain timeout or delay after every GET request. I am facing difficulty in where exactly to add the timeout.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
kartik
  • 666
  • 5
  • 21

3 Answers3

4

If you want to make requests in serial, you shouldn't use Promise.all, which initializes everything in parallel - better to use a reduce that awaits the previous iteration's resolution and awaits a promise-timeout. For example:

async function getAllSearchResultProfiles(searchAjaxUrl) {
  const ajaxUrls = [];
  for (let i = 0; i < numResults; i += resultsPerPage) {
    ajaxUrls.push(searchAjaxUrl + "&start=" + i);
  }
  const responses = await ajaxUrls.reduce(async (lastPromise, url) => {
    const accum = await lastPromise;
    await new Promise(resolve => setTimeout(resolve, 1000));
    const response = await getSearchResultsForOnePage(url);
    return [...accum, response];
  }, Promise.resolve([]));

  // do stuff with responses
  const searchProfiles = responses.map(response => (
    response.meta.total === 0
    ? []
    : response.result.searchResults
  ));
  return [].concat(...searchProfiles);
}

Note that only asynchronous operations should be passed from one .then to another; synchronous code should not be chained with .then, just use variables and write the code out as normal.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • How would you accumulate the responses before you do responses.map? – kartik May 18 '18 at 04:30
  • That's what the `reduce` accomplishes - it `await`s all responses before proceeding. – CertainPerformance May 18 '18 at 04:31
  • Can you elaborate on `return[...accum, response]`. What does that do exacty? – kartik May 18 '18 at 04:49
  • `...` is spread syntax - the interpreter replaces `...arrayLike` with `arrayLike[0], arrayLike[1], arrayLike[2]` etc. It's basically a better version of `concat`. `[...accum, response]` is equivalent to `accum.concat(response)`. – CertainPerformance May 18 '18 at 04:51
  • When I do that, I get `Uncaught (in promise) TypeError: accum is not iterable` and `Uncaught (in promise) TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined` – kartik May 18 '18 at 05:00
  • Got it. I should have `return accum.concat([response]);` and `return [].concat.apply([], searchProfiles);` – kartik May 18 '18 at 05:03
  • I forgot to put the initial value for the reducer - use `Promise.resolve([])` instead of `Promise.resolve()` or the first iteration's `accum` won't know what to start with. – CertainPerformance May 18 '18 at 05:08
  • I have a query, how would I fit another request where I get the value for variable `numResults` in the for loop from another GET request? – kartik May 29 '18 at 20:03
  • `await` a Promise inside the loop. Or, if you want to `await` in parallel, use `Promise.all`. – CertainPerformance May 29 '18 at 21:34
  • I am confused about the loop. It is just one GET request which is going to fetch the value of `numResults`. The logic below should be carried out after fetching the value from this request. @CertainPerformance – kartik May 30 '18 at 01:40
  • For example, `const numResults = await getNumResults()` and then continue with the rest of the code – CertainPerformance May 30 '18 at 10:54
1

I find a simple for loop in an async function to be the most readable, even if not necessarily the most succinct for things like this. As long as the function is an async function you can also create a nice pause() function that makes the code very easy to understand when you come back later.

I've simplified a bit, but this should give you a good idea:

function pause(time) {
  // handy pause function to await
  return new Promise(resolve => setTimeout(resolve, time))
}
async function getAllSearchResultProfiles(searchAjaxUrl) {
  var ajaxUrls = [];
  for (var i = 0; i < 5; i++) {
    ajaxUrls.push(searchAjaxUrl + "&start=" + i);
  }
  let responses = []
  for (url of ajaxUrls) {
    // just loop though and await

    console.log("sending request")
    let response = await getSearchResultsForOnePage(url)

    console.log("recieved: ", response)
    responses.push(response)
    await pause(1000) // wait one second

  }
  //responses.map() and other manilpulations etc...
  return responses
}

function getSearchResultsForOnePage(url) {
  //fake fetch
  return Promise.resolve(url)
}
getAllSearchResultProfiles("Test")
  .then(console.log)
Mark
  • 90,562
  • 7
  • 108
  • 148
-1

If you want to add a delay in every request then add a setTimout() in your function which fetches data from api

function getSearchResultsForOnePage(url) {
  return new Promise((resolve, reject) => {
   fetch(url, {
      credentials: 'include'
   })
   .then(response => reresponse.json())
   .then(data => {
     let timeout = 1000;
     setTimeout(() => resolve(data), timeout);
  });
 }
Zohaib Ijaz
  • 21,926
  • 7
  • 38
  • 60