Reputation:
I have an asynchronous function that I would like to abort from a process that is running in the background. The abortion does not need to cancel any promises that are currently pending inside of the asynchronous function. It just needs to make the function return and prevent any further lines from getting executed. How could I do this? The current code does not work because the error is thrown inside setInterval instead of the try block.
setInterval(() => { //background proccess
abort();
}, 5000);
async function func() {
try {
abort = function thr() {
throw new Error("dsaf");
};
await new Promise((r) => setTimeout(r, 10000));
await new Promise((r) => setTimeout(r, 10000));
await new Promise((r) => setTimeout(r, 10000));
await new Promise((r) => setTimeout(r, 10000));
await new Promise((r) => setTimeout(r, 10000));
await new Promise((r) => setTimeout(r, 10000));
await new Promise((r) => setTimeout(r, 10000));
} catch {
console.log("caught");
}
}
func()
Update: Are there other languages that support this type of pattern better or are all languages like this?
Upvotes: 0
Views: 315
Reputation: 24191
To make life easier, you could create a helper function.
Below I've created a simple abortable function, all you do then is put your async code inside a run
, and then call abort
when your ready.
eg.
const sleep = ms => new Promise(r => setTimeout(r, ms));
function abortable() {
let aborted = false;
return {
abort: () => aborted = true,
run: async cb => {
if (aborted) throw new Error('Aborted');
return await cb();
}
}
}
async function hello(caption, ms) {
await sleep(ms);
return `Ran ${caption}`;
}
const {abort, run} = abortable();
setTimeout(() => abort(), 2000);
async function test() {
try {
console.log(await run(() => hello('One', 1500)));
console.log(await run(() => hello('Two', 1500)));
//this next one should not run, as it's over 2000ms
console.log(await run(() => hello('Three', 1500)));
} catch (e) {
console.log(e.message);
}
}
test();
Using generators, like in @Dmitriy's answer with CPromise's looks nice, only slight problem is that if the return value from the previous one is required for the next. A simple solution here though is use some form of state and pass that to each function.
Below is an example using generators, but without any 3rd party lib.
const sleep = ms => new Promise(r => setTimeout(r, ms));
function abortable() {
let aborted = false;
return {
abort: () => aborted = true,
run: async p => {
for (const r of p()) {
if (aborted) throw new Error('aborted');
console.log(await r);
}
}
}
}
async function hello(state, value, ms) {
await sleep(ms);
state.total += value;
return `Total ${state.total}`;
}
const {abort, run} = abortable();
setTimeout(() => abort(), 2000);
run(function *() {
const state = {total: 0};
yield hello(state, 5, 1500);
yield hello(state, 10, 1500);
yield hello(state, 15, 1500);
}).catch(e => console.log(e.message));
Upvotes: 0
Reputation: 1597
Using the CPromise package, this can be done as follows (Live sandbox):
import { CPromise } from "c-promise2";
const func = CPromise.promisify(function* () {
try {
yield new Promise((r) => setTimeout(r, 10000));
yield new Promise((r) => setTimeout(r, 10000));
yield new Promise((r) => setTimeout(r, 10000));
yield new Promise((r) => setTimeout(r, 10000));
yield new Promise((r) => setTimeout(r, 10000));
yield new Promise((r) => setTimeout(r, 10000));
yield new Promise((r) => setTimeout(r, 10000));
} catch (err) {
console.log(`caught: ${err}`);
}
});
const promise = func();
setInterval(() => {
promise.cancel();
}, 5000);
Upvotes: 0
Reputation: 664538
You're probably looking for
let throwIfAborted = () => {};
setInterval(() => { //background proccess
throwIfAborted = () => { throw new Error("Abort"); };
}, 5000);
async function func() {
try {
throwIfAborted();
await new Promise((r) => setTimeout(r, 10000));
throwIfAborted();
await new Promise((r) => setTimeout(r, 10000));
throwIfAborted();
await new Promise((r) => setTimeout(r, 10000));
throwIfAborted();
await new Promise((r) => setTimeout(r, 10000));
throwIfAborted();
await new Promise((r) => setTimeout(r, 10000));
throwIfAborted();
await new Promise((r) => setTimeout(r, 10000));
throwIfAborted();
await new Promise((r) => setTimeout(r, 10000));
throwIfAborted();
} catch {
console.log("caught");
}
}
func()
Upvotes: 1