Initial commit

master
Kuba Orlik 4 years ago
commit 10f5a19840

@ -0,0 +1,40 @@
# Abortable promises ⚡
A module for node that lets you run complex sequence of async functions and:
1. Use an external signal to stop the sequence of steps
```typescript
const sleep = promisify((timeout: number, callback: (...args: any[]) => void) =>
setTimeout(callback, timeout)
);
const a = new AbortablePromise(async function* () {
yield await sleep(1000);
console.log("awaited 100");
yield await sleep(2000);
console.log("awaited 200");
yield await sleep(3000);
console.log("awaited 300");
});
setTimeout(() => a.abort(), 1500); // doesn't get to sleep(3000)
```
2. Use an external signal to kill an already started asynchronous step
```typescript
const b = new AbortablePromise(async function* (await_or_abort) {
const ping = await deferedSpawn("ping", ["8.8.8.8"]);
while (true) {
console.log(
await await_or_abort(ping.waitForNextData(), () => {
ping.kill();
})
);
yield;
}
});
setTimeout(() => b.abort(), 5000); // doesn't get to 5th ping
```

@ -0,0 +1,23 @@
{
"name": "abortable-promise",
"version": "0.1.0",
"description": "Abortable promises based on async generators",
"main": "./lib/index.js",
"types": "./@types/index.d.ts",
"scripts": {
"build": "tsc",
"watch": "npm run build -- --watch",
"install": "npm run build",
"prepare": "npm run build"
},
"repository": "gitea@git.kuba-orlik.name:kuba/async-spawner.git",
"author": "Kuba Orlik",
"license": "ISC",
"dependencies": {
"@types/node": "^14.14.19",
"emittery": "^0.8.0"
},
"devDependencies": {
"typescript": "^4.1.3"
}
}

@ -0,0 +1,93 @@
import { deferedSpawn } from "async-spawner";
import Emittery from "emittery";
import { promisify } from "util";
//needed to pass information on wether or not the promise has been aborted witout 'this'
class AbortableEmittery extends Emittery {
public aborted = false;
abort() {
this.aborted = true;
this.emit("abort");
}
}
class AbortablePromise<T> extends Promise<T | null> {
private emitter: AbortableEmittery;
constructor(
make_generator: (
await_or_abort: (
promise: Promise<any>,
on_abort: () => void
) => Promise<void>
) => AsyncGenerator
) {
const emitter = new AbortableEmittery();
const await_or_abort = (promise: Promise<any>, on_abort: () => void) => {
return new Promise((resolve, reject) => {
let resolved = false;
emitter.on("abort", () => {
if (!resolved) {
on_abort();
resolve(null);
resolved = true;
}
});
promise
.then((result) => {
if (!resolved) {
resolve(result);
resolved = true;
}
})
.catch(reject);
});
};
super(async (resolve, reject) => {
let step_value;
const generator = make_generator(await_or_abort);
do {
if (emitter.aborted) {
resolve(null);
return;
}
step_value = await generator.next();
} while (!step_value.done);
});
this.emitter = emitter;
}
abort() {
this.emitter.abort();
}
}
// const sleep = promisify((timeout: number, callback: (...args: any[]) => void) =>
// setTimeout(callback, timeout)
// );
// const a = new AbortablePromise(async function* () {
// yield await sleep(1000);
// console.log("awaited 100");
// yield await sleep(2000);
// console.log("awaited 200");
// yield await sleep(3000);
// console.log("awaited 300");
// });
// setTimeout(() => a.abort(), 1500);
const b = new AbortablePromise(async function* (await_or_abort) {
const ping = await deferedSpawn("ping", ["8.8.8.8"]);
while (true) {
console.log(
await await_or_abort(ping.waitForNextData(), () => {
ping.kill();
})
);
yield;
}
});
setTimeout(() => b.abort(), 5000);

@ -0,0 +1,19 @@
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"target": "ES6",
"declaration": true,
"esModuleInterop": true,
"lib": ["es6", "esnext"],
"outDir": "lib",
"checkJs": true,
"allowJs": true,
"declarationDir": "@types",
"sourceMap": true
},
"include": ["src/**/*"]
}
Loading…
Cancel
Save