0

I was wondering if there is any to cancel / stop execution of a javascript function that contains multiple await functions. Due to the nature of promises and their lack of proper cancellations, is there any other implementation or library to help me achieve something like this?

async function run(x,y,z) {
   return new Promise(async(resolve,reject) => {  
        await doSomething(x)
        await doSomething(y)
        //cancel could be happen around here and stop the last "doSomething"
        await doSomething(z)
   })
}

setTimeout(() => {
     run.cancel()
},500) //cancel function after 500ms


Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Not with promises. Observables would be something you want to look into. – Seti Feb 16 '22 at 00:26
  • What's in your actual `doSomething()`. Please show **real code**, not theoretical code as we can help you much better. How to abort something in nodejs or in the browser depends entirely upon what the operation is. There is NO generic answer to aborting. – jfriend00 Feb 16 '22 at 01:09
  • Also, wrapping existing promise-returning operation in your own new promise is a promise anti-pattern. Should not be doing that. – jfriend00 Feb 16 '22 at 01:10
  • @jfriend00 The doSomething() functions are mostly fetch requests and data parsing those requests which can take around 250 - 1000ms per function – user16180718 Feb 16 '22 at 01:28
  • @Seti The problem with observers is it doesn't actually stop code execution but rather gives a window to when a value can be returned. – user16180718 Feb 16 '22 at 01:35
  • @jfriend00 I'm essentially trying to completely stop code execution in the function, as if the function was in a web worker and then you called worker.terminate() – user16180718 Feb 16 '22 at 01:39
  • @user16180718 - Well, there is no way to do that in Javascript unless you do what you suggested in a webWorker (in the browser) or workerThread or child_process (in nodejs). The best you can do is modify a flag that you can check during your processing of asynchronous results and adjust your code execution based on that flag. – jfriend00 Feb 16 '22 at 01:42
  • @jfriend00 That is what I worried, I essentially need JS to run this function over 1k in parallel. I was using webworkers and would just terminate the function when it needed to be killed. Would an abortcontroller still not work for this? – user16180718 Feb 16 '22 at 01:49
  • What does "over 1k in parallel" mean? I don't know what that is. I've provided an answer based on the example in your question. We can't possibly provide answers that anticipate other things that are not described in your question. – jfriend00 Feb 16 '22 at 01:49
  • @jfriend00 The way my program works is it holds around 1000 of the "run" function in an array, inside of a webworker. This all runs in a parallel on the worker and the program needs to kill one of the function every so often. – user16180718 Feb 16 '22 at 01:53
  • @user16180718 - Geez. So, if that's the real problem, then WHY didn't you put that into your question? I repeat, we can't possibly provide answer for things that are NOT in your question. Please post questions with REAL code. Please do not post questions with pseudo code. We can ALWAYS help you better when we can see the real code. For some unknown reason, people thing they have to abstract their question with pseudo-code here on stackoverflow. That only makes it harder to help you. – jfriend00 Feb 16 '22 at 02:00
  • At this point, I've written an answer based on what's in your question. That's my best shot at helping given what's in your actual question. – jfriend00 Feb 16 '22 at 02:01
  • @jfriend00 Thanks for trying to help, however this looks like a limitation with javascript and there is nothing I can do. – user16180718 Feb 16 '22 at 02:06
  • If we could see the real code for the real problem you're trying to solve (perhaps in a new question at this point), then we'd have a chance at applying whatever Javascript does know how to do to that specific problem. – jfriend00 Feb 16 '22 at 02:11
  • @jfriend00 No matter how I rephrase the question the limitations of JS are clearly shown. Also sometimes code cannot be shared on the internet, it looks like I'm going to rewrite this section of my program for WASM instead. – user16180718 Feb 16 '22 at 02:23
  • [Never pass an `async function` as the executor to `new Promise`](https://stackoverflow.com/q/43036229/1048572)! – Bergi Feb 16 '22 at 02:27
  • @Bergi What is the cons of this? – user16180718 Feb 16 '22 at 03:13
  • @Bergi - Kind of a pointless point here anyway since (as I know you already know), it's an anti-pattern to wrap existing promises in another promise. Isn't that the better thing to educate on here? – jfriend00 Feb 16 '22 at 03:59
  • @jfriend00 Yes, this is a special case of the promise constructor antipattern, and unfortunately common enough that it got its own canonical explanation. That explanation even starts with a link to the general case – Bergi Feb 16 '22 at 13:21

2 Answers2

0

The way to actually use cancellation is through the AbortController which is available in the browser and on Node 15+

Node reference: https://nodejs.org/api/globals.html#class-abortcontroller

MDN reference: https://developer.mozilla.org/en-US/docs/Web/API/AbortController

Some APIs are currently using out of the box the abort signal like fetch in the browser or setTimeout timers API in Node (https://nodejs.org/api/timers.html#timerspromisessettimeoutdelay-value-options).

For custom functions/APIs you need to implement it by yourself but it's highly encouraged to follow the Abort signal methodology so you can chain both custom and oob functions and make use of a single signal that does not need translation

anros
  • 26
  • 2
  • So I would essentially apply the abort signal to the "run" function. Thus completely stopping code execution for that function? – user16180718 Feb 16 '22 at 01:27
  • @user16180718 - Except there is no mechanism in Javascript for "stopping code execution for that function". The AbortController is just an API to follow to expose an abort operation. It's your own code that has to figure out how to actually abort something. – jfriend00 Feb 16 '22 at 01:56
0

To just stop the advancement from one function call to the next, you can do something like this:

function run(x, y, z) {
    let stop = false;
    async function run_internal() {
        await doSomething(x)
        if (stop) throw new Error("cancelled");
        await doSomething(y)
        if (stop) throw new Error("cancelled");
        await doSomething(z)
    }
    return {
        cancel: () => {
            stop = true;
        },
        promise: run_internal();
    };
}

const retVal = run(a, b, c);
retVal.promise.then(result => {
    console.log(result);
}).catch(err => {
    console.log(err);
})

setTimeout(() => {
    retVal.cancel()
}, 500); //cancel function after 500ms

Javascript does not have a generic way to "abort" any further execution of a function. You can set a flag via an external function and then check that flag in various points of your function and adjust what you execute based on that flag.

Keep in mind that (except when using workerThreads or webWorkers), Javascript runs your code in a single thread so when it's running, it's running and none of your other code is running. Only when it returns control back to the event loop (either by returning or by hitting an await) does any of your other code get a chance to run and do anything. So, "when it's actually running", your other code won't be running. When it's sitting at an await, your other code can run and can set a flag that can be checked later (as my example above shows).

fetch() in a browser has some experimental support for the AbortController interface. But, please understand that once the request has been sent, it's been sent and the server will receive it. You likely won't be aborting anything the server is doing. If the response still hasn't come back yet or is in the process of coming back, your abort may be able to interrupt that. Since you can't really know what is getting aborted, I figure it's better to just put a check in your own code so that you won't process the response or advance to further processing based on a flag you set.

You could wrap this flag checking into an AbortController interface if you want, but it doesn't change the fundamental problem in any way - it just affects the API you expose for calling an abort.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • While I'd rather stay away from this approach, it seems like the only one in which can work with JS and custom async functions. – user16180718 Feb 16 '22 at 02:06