0

I have function which starts many promise based functions in loop, for example:

let doSomething = (page, a, b) => {
    return new Promise(async(resolve, reject) => {

        eventBus.on('game::lag', () => {
            throw new Error('Game lag');
        });

        while(a.length > 0) {
            await doSomethingAsync();
            await doSomething2Async();

            while(b.length > 0) {
                await doSomething3();
                b = await getAsyncB();  
            }    
            a = await getAsyncA();  
        }
        resolve();
    });
};

Now on custom event, which come from other part of program, I want this script to die and stop every nested functions (doSomething* functions). Additionally I have some intervals within doSomething* functions but I want everything to stop also intervals.

I tried like in example code, error is thrown and catched in external file succesfully. However I can see doSomething* scripts still runing.

Is it normal, that nested functions are still runinng although I thrown error from "parent"?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
BT101
  • 3,666
  • 10
  • 41
  • 90
  • Possible duplicate of [Cancel a vanilla ECMAScript 6 Promise chain](https://stackoverflow.com/questions/29478751/cancel-a-vanilla-ecmascript-6-promise-chain) – str Apr 30 '19 at 19:26
  • [Never pass an `async function` as the executor to `new Promise`](https://stackoverflow.com/q/43036229/1048572)! – Bergi Apr 30 '19 at 21:40

3 Answers3

1

It's normal, that error thrown inside listener doesn't affect the async function, cus they are not in the same call stack.

If you want everything to stop on recieving your signal, you better use something like generator function to gain more fine grain control.

hackape
  • 18,643
  • 2
  • 29
  • 57
1

The exception isn't thrown from a "parent", it is thrown within the event handler. That has nothing to do with the execution of the async function. You would need something like

async function doSomething(page, a, b) {
    const lagError = new Promise((resolve, reject) => {
        eventBus.on('game::lag', () => {
            reject(new Error('Game lag'));
        });
    });

    while(a.length > 0) {
        await Promise.race([lagError, doSomethingAsync()]);
        await Promise.race([lagError, doSomething2Async()]);

        while(b.length > 0) {
            await Promise.race([lagError, doSomething3()]);
            b = await Promise.race([lagError, getAsyncB()]);
        }    
        a = await Promise.race([lagError, getAsyncA()]);
    }
}

Alternatively, you can set an error flag from the handler and do an if (error) throw new Error(…) check between every two asynchronous actions.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • doSomething is also returning promise so I tried instead of adding promise.race to every method inside doSomething, just add promise.race to external file where i invoke doSomething, but for some reason I still can see interval in doSomething2Async running, not stoping :( I don't get it – BT101 May 01 '19 at 14:03
  • eg. `await Promise.race([lagError, doSomethingAsync()]);` is it really stoping doSomethingAsync when lagError is rejected? Or just ignoring results of doSomethingAsync? – BT101 May 01 '19 at 14:05
  • No, `Promise.race` doesn't stop anything, you need to do that explicitly. `Promise.race` just gets the first result from any promise, and then ignores the rest, so that when `lagError` gets rejected while something else is still running it immediately throws the exception from `await` and your `doSomething` function finishes. – Bergi May 01 '19 at 16:25
  • To make `doSomethingAsync` stop, you a) need to be able to stop the task at all, e.g. clear a timeout, and b) you need a way to signal the function to stop. Cancellation tokens are a common way to achieve this. Have a look at [my fork of the `Creed` promise library](https://github.com/bergus/creed/blob/cancellation/cancellation.md) for an example of how to do that. – Bergi May 01 '19 at 16:27
  • Yeah but your a) does not sounds good for me because I have much more promises inside while loop and lots of them got intervals inside so I would need to watch for event in many different files and basically repeat my code (DRY). I'm gonna dive into your library, thanks for sharing, let's hope it will help me. – BT101 May 01 '19 at 19:39
  • Instead of watching for that `game::lag` event everywhere, take a cancellation token as an argument and watch it. You can even write a generic cancellable-interval function. Then have `doSomething` create a token that signals cancellation when the `game::lag` event occurs, and pass that token to all asynchronous stuff that `doSomething` starts. – Bergi May 01 '19 at 20:02
  • I have problem with implementation your package. I installed `"creed": "^1.4.0",` and entire code I have currently is: `import { delay, CancelToken } from 'creed'; const token = new CancelToken();` and this already give `error TypeError: creed__WEBPACK_IMPORTED_MODULE_5__.CancelToken is not a constructor`. Also my IDE is saying it can not resolve symbol CancelToken: https://i.imgur.com/7AaRu0m.png – BT101 May 02 '19 at 16:45
  • @BT101 Sorry, I didn't update the readme. I didn't publish the package on npm, `creed` is the [original library](http://blog.briancavalier.com/creed/) without any cancellation features. You'd need to install Creed from the source if you wanted to use my fork. – Bergi May 02 '19 at 18:37
  • Well so I download it as zip and extract dist/creed.min.js. Then I `import { cancelToken } from './../../../creed/creed.min'; console.log(cancelToken);` and cancelToken is undefined. Am I doing something wrong? – BT101 May 03 '19 at 22:00
  • I also tried extracting entire src folder of your package and then importing CancelToken only but it doesn't work also. Could you please provide some tips how to use this fork? – BT101 May 03 '19 at 22:09
  • This is weird because I can clearly see that CancelToken is a class, but it keep saying `TypeError: _creed_src_CancelToken__WEBPACK_IMPORTED_MODULE_5__.CancelToken is not a constructor` – BT101 May 03 '19 at 22:18
  • @BT101 Hm, importing `CancelToken` (not `cancelToken`) from `creed` (i.e. its `main` module) [should work](https://github.com/bergus/creed/blob/cancellation/src/main.js#L13). To import from `/dist/creed.min.js`, you probably will have to run a build first. – Bergi May 04 '19 at 11:19
0

something like that maybe, when action is set to false stop what you re doing in the async function

let action = true
await doSomethingAsync(action)
async doSomethingAsync(action) { if(!action) stopWhatYouDo }
CodInBox
  • 16
  • 1