1

I found this code in a project:

const fn = async () => {
    let x = 0;
    for(let i = 0; i < 50; i++){
        const res = await api.call(i);
        if(res.someProp) x++;
    }

    return x;
}

I want to be able to stop it mid way, so that if I call it again, it will start from scratch and discard the previous call results. To avoid making two sets of requests at the same time.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
nick
  • 2,819
  • 5
  • 33
  • 69
  • 2
    Does this answer your question? [Promise - is it possible to force cancel a promise](https://stackoverflow.com/questions/30233302/promise-is-it-possible-to-force-cancel-a-promise) – John Montgomery Nov 09 '20 at 20:10
  • You cannot stop await call in mid way. You can just lock it to prevent duplicate call at one time using a flag variable. – Prime Nov 09 '20 at 20:11
  • Using async & await in JS is basically a syntactical sugarcoat over using Promises and then( ). Promises once fired cannot be stopped midway. However, if it's any consolation synchronous code always executes first, after which the microtasks like promises are executed and finally the callbacks from callback queue get executed, so although this API call will definitely add some load onto your application, it will be asynchronous and a background process. – Link Nov 09 '20 at 20:20
  • 1
    You can `break;` out of a for loop, so what you can do is check a global flag variable (like `stopLoop`) inside the loop, and break if it's true. If you want the loop to start over, you can also reset x and i to 0 inside the loop (and `stopLoop` to `false`). –  Nov 09 '20 at 20:24
  • @ChrisG I think the idea is that what if it's waiting for `api.call` to complete, and the code that originally called `fn` decides to give up. How can it cancel the whole chain without explicitly passing in a participatory cancellation object that every api beneath must support. At some point it's likely to be waiting for a low-level async call, that wouldn't participate. So I think the question is asking how to tear down the promise chain and cancel it. (FWIW, I think the answer is you can't, you must add some explicit cancellation handling at the levels that can do so.) – Wyck Nov 09 '20 at 20:27

2 Answers2

1

This should do:

let token;
const fn = async () => {
    const my = token = Symbol();
    let x = 0;
    for(let i = 0; i < 50 && my == token; i++){
        const res = await api.call(i);
        if(res.someProp) x++;
    }

    return x;
}

While there still can be some overlap between the calls, any previous loops will break their iteration as soon as the next fn() call is started.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • This is a great answer, and I've no idea why this has had no upvotes or comments in over a year! – Neo Dec 12 '21 at 17:18
0

You can use any technique of using an external flag variable to break the loop.

As a workaround you can try to use a custom Promise class (Live demo):

import CPromise from "c-promise2";

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

async function api(i) {
  console.log(`Async API call [${i}]`);
  await delay(100);
  return {};
}

const fn = () =>
  CPromise.from(function* () {
    let x = 0;
    for (let i = 0; i < 50; i++) {
      const res = yield api.call(i);
      if (res.someProp) x++;
    }

    return x;
  });

const cancelablePromise = fn().then(
  () => console.log("Done"),
  (err) => console.log(`Fail: ${err}`) // Fail: CanceledError: canceled 
);

setTimeout(() => {
  cancelablePromise.cancel(); // abort the async sequence (loop) after 3500ms
}, 3500);
Dmitriy Mozgovoy
  • 1,419
  • 2
  • 8
  • 7