1

I am used to await but eslint hates it in a loop. Eslint does not like defining a function in a loop as well. Therefore I ended up with the following snippet in Vuex script:

  // wait until the file is synchronized
  let waitTime = 100;
  let fileExists = false;
  const checkSync = (res, i) => {
    console.log(res.status);
    if (res.status === 200) {
      fileExists = true;
      console.log('Status 200, setting fileExists=true');
    } else if (res.status === 404) {
      Vue.$log.debug(`Image not ready for download ${res.status}`);
      setTimeout(() => { waitTime = (i < 4) ? 200 : 1000; }, waitTime);
      console.log(`waitTime = ${waitTime}`);
    }
  };
  for (let i = 0; !fileExists && i < 5; i += 1) {
    console.log(`fileExists = ${fileExists}`);
    axios.head(`${response.data.data.url}`).then(resp => checkSync(resp, i));
  }

But the log reveals that the first log statement inside the loop is executed one by one and the second statement with a promise/resolve is executed when the loop is finished. I could rewrite the code to await and ignore eslint, but I hope I finally get deeper knowledge of good old promises.

fileExists = false
fileExists = false
fileExists = false
fileExists = false
fileExists = false
items.js:175 200
items.js:178 Status 200, setting fileExists=true
items.js:175 200
items.js:178 Status 200, setting fileExists=true
items.js:175 200
items.js:178 Status 200, setting fileExists=true
items.js:175 200
items.js:178 Status 200, setting fileExists=true
items.js:175 200
items.js:178 Status 200, setting fileExists=true

UPDATE

The way I do it usually. Eslint complains but the code is triggered by a users when they upload new picture so I do not need to care about efficiency.

  // wait until the file is synchronized on backend to www node
  let waitTime = 100;
  for (let i = 0; i < 5; i += 1) {
    // eslint-disable-next-line no-await-in-loop
    const head = await axios.head(response.data.data.url);
    if (head.status === 200) {
      return response.data;
    } else if (head.status === 404) {
      Vue.$log.debug(`Image not ready for download ${head.status}`);
      // eslint-disable-next-line no-loop-func
      setTimeout(() => { waitTime = (i < 4) ? 200 : 1000; }, waitTime);
      console.log(`waitTime = ${waitTime}`);
    }
  }
Leos Literak
  • 8,805
  • 19
  • 81
  • 156
  • What does the `await` version look like? – John Kugelman Sep 04 '21 at 14:38
  • _"I am used to await..."_ - Which would require the knowledge how `Promise`s work o.O _"eslint hates it in a loop"_ - Exactly because of the problem you have with your script... – Andreas Sep 04 '21 at 14:38
  • 1
    `await` in a loop is EXACTLY the solution you need in order to process asynchronous requests one after the other and wait for the last before making the next. ESLint doesn't like it because it's very inefficient and almost always unnecessary. Is there some reason you can't run the requests simultaneously, either with Promise.all or with something like what you have above? It's not clear to me exactly what you're trying to do that requires waiting for each request individually. But if you really need that, just ignore ESLint and use await in a loop... – Robin Zigmond Sep 04 '21 at 14:41
  • @JohnKugelman That won't change much, because `checkSync()` (despite its name) is neither "sync" nor does it return something the script could `await` for (`setTimeout()`) – Andreas Sep 04 '21 at 14:41
  • @Andreas In its current form, I agree, but maybe it was better before the OP tried to remove `await`. – John Kugelman Sep 04 '21 at 14:45
  • Hi, this code is a part of upload process and it needs to wait until the server side synchronization between two machines is finished (lsyncd). So my intention was to run up to five HEAD requests with some delay and once I get 200, the code may go on and return the URL. Otherwise Chrome will try to download a picture, nginx will return 404 and Chrome will never download the picture again. See https://github.com/literakl/mezinamiridici/issues/213#issuecomment-908608727 – Leos Literak Sep 04 '21 at 14:54
  • 1
    `setTimeout(() => { waitTime = (i < 4) ? 200 : 1000; }, waitTime);` doesn't do anything. If the intention is to sleep you need an [await](https://stackoverflow.com/questions/33289726/combination-of-async-function-await-settimeout). – John Kugelman Sep 05 '21 at 13:19

1 Answers1

2
// This function does promised function from array one-by-one
const reducePromise = (items = [], callback) => (
  items.reduce(async (promisedAcc, curr) => {
    const acc = await promisedAcc;
    const currentResult = await callback(curr);
    return acc.concat(currentResult);
  }, Promise.resolve())
)

// This Function resolves promise after time that passed by param
const wait = (time = 0) => new Promise((resolve) => setTimeout(() => resolve(), time))

let fileExists = false;

reducePromise([...Array(5).keys()], async (i) => {
  await axios.head(`${response.data.data.url}`)
  .then(res => {
    fileExists = true;
    console.log('Status 200, setting fileExists=true');
  })
  .catch(({ response: res }) => {
    Vue.$log.debug(`Image not ready for download ${res.status}`);
    const waitTime = (i < 4) ? 200 : 1000;
    return wait(waitTime)
  })
})

https://developers.google.com/web/fundamentals/primers/async-functions#example_outputting_fetches_in_order

Bad Dobby
  • 811
  • 2
  • 8
  • 22