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.

5
  • 2
    Does this answer your question? Promise - is it possible to force cancel a promise Commented Nov 9, 2020 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. Commented Nov 9, 2020 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. Commented Nov 9, 2020 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). Commented Nov 9, 2020 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.) Commented Nov 9, 2020 at 20:27

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

1 Comment

This is a great answer, and I've no idea why this has had no upvotes or comments in over a year!
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);

Comments

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.