@jsenv/abort
Advanced tools
+2
-0
| export { Abort } from "./src/abort.js" | ||
| export { createCallbackList } from "./src/callback_list.js" | ||
| export { raceCallbacks } from "./src/callback_race.js" |
+3
-0
| export { Abort } from "./src/abort.js" | ||
| export { createCallbackList } from "./src/callback_list.js" | ||
| export { raceCallbacks } from "./src/callback_race.js" | ||
| // exports specific to Node.js | ||
| export { raceProcessTeardownEvents } from "./src/process_teardown_events.js" |
+2
-3
| { | ||
| "name": "@jsenv/abort", | ||
| "version": "0.0.1", | ||
| "version": "1.0.0", | ||
| "description": "Help to write code compatible with abort signals", | ||
@@ -19,4 +19,3 @@ "license": "MIT", | ||
| "publishConfig": { | ||
| "access": "public", | ||
| "registry": "https://registry.npmjs.org" | ||
| "access": "public" | ||
| }, | ||
@@ -23,0 +22,0 @@ "type": "module", |
+23
-16
@@ -5,10 +5,9 @@ # Abort [](https://www.npmjs.com/package/@jsenv/abort) [](https://github.com/jsenv/abort/actions?workflow=main) [](https://codecov.io/gh/jsenv/abort) | ||
| - API designed to properly cleanup things (event listeners, timeouts, ...) | ||
| - Prevents memory leak | ||
| - No maxListeners warning for `"abort"` event on Node.js | ||
| - API cleanup things by default | ||
| - Removing event listeners, clearing timeouts, ... | ||
| - Allow to safely bypass max listeners warning when needed | ||
| - Code can do 20 abortable fetch requests in parallel without warnings | ||
| - Manage multiple abort signals | ||
| - Compose abort signals from multiple sources | ||
| - Code can react differently according to which signal aborted the operation | ||
| - Allow to bypass safely max listeners warning when a single signal NEEDS to be listened lot of times | ||
| - No "potential memory leak" when code does 20 http requests in parallel | ||
| - Code can do differents things depending on which signal aborted the operation | ||
@@ -30,7 +29,11 @@ # Example | ||
| The code above forwards an abort signal to `customFetch`. Code below shows how `"@jsenv/abort"` helps to manage the received signal. | ||
| Code above pass an abort signal to `customFetch`. | ||
| Let's see how `"@jsenv/abort"` helps to manage the signal received. | ||
| _fetch_custom.mjs_ | ||
| ```js | ||
| import { Abort } from "@jsenv/abort" | ||
| export const customFetch = ( | ||
@@ -58,15 +61,19 @@ url, | ||
| try { | ||
| const response = await fetch(url, { signal }) | ||
| const response = await fetch(url, { signal: fetchOperation.signal }) | ||
| return response | ||
| } catch (e) { | ||
| if (SIGINTAbortSource.signal.aborted) { | ||
| // aborted by SIGINT | ||
| if (Abort.isAbortError(e)) { | ||
| if (SIGINTAbortSource.signal.aborted) { | ||
| // aborted by SIGINT | ||
| } | ||
| if (timeoutAbortSource.signal.aborted) { | ||
| // aborted by timeout | ||
| } | ||
| if (signal.aborted) { | ||
| // aborted from outside | ||
| } | ||
| } | ||
| if (timeoutAbortSource.signal.aborted) { | ||
| // aborted by timeout | ||
| } | ||
| if (signal.aborted) { | ||
| // aborted from outside | ||
| } | ||
| throw e | ||
| } finally { | ||
| await fetchOperation.clean() | ||
| } | ||
@@ -73,0 +80,0 @@ } |
+45
-27
@@ -16,4 +16,2 @@ /* | ||
| }, | ||
| // maybe reintroduce fromSignal? | ||
| } | ||
@@ -37,3 +35,3 @@ | ||
| callbacks.forEach((callback) => { | ||
| callback("operation aborted") | ||
| callback(FROM_ABORT) | ||
| }) | ||
@@ -55,3 +53,10 @@ } | ||
| const gracefulStop = async (reason) => { | ||
| const clean = async () => { | ||
| // do not trigger abortCallbackList, we'll do it | ||
| operationSignal.onabort = null | ||
| // but do call any remaining "abort" callback still registered on operation signal | ||
| // This ensure they are properly executed and do not stay | ||
| // here listening forever (especially when registered with {once: true}) | ||
| operationAbortController.abort() | ||
| const callbacks = abortCallbackList.copy() | ||
@@ -61,3 +66,3 @@ abortCallbackList.clear() | ||
| callbacks.map(async (callback) => { | ||
| await callback(reason) | ||
| await callback(FROM_CLEAN) | ||
| }), | ||
@@ -69,6 +74,3 @@ ) | ||
| if (operationSignal.aborted) { | ||
| return { | ||
| signal, | ||
| cancel, | ||
| } | ||
| return cancel | ||
| } | ||
@@ -78,6 +80,3 @@ | ||
| operationAbortController.abort() | ||
| return { | ||
| signal, | ||
| cancel, | ||
| } | ||
| return cancel | ||
| } | ||
@@ -111,8 +110,5 @@ | ||
| return { | ||
| signal, | ||
| cancel: () => { | ||
| cancelRace() | ||
| cancel() | ||
| }, | ||
| return () => { | ||
| cancelRace() | ||
| cancel() | ||
| } | ||
@@ -131,3 +127,7 @@ } | ||
| return addAbortSignal(abortSourceSignal, cancelEffect) | ||
| const removeAbortSignal = addAbortSignal(abortSourceSignal, cancelEffect) | ||
| return { | ||
| signal: abortSourceSignal, | ||
| remove: removeAbortSignal, | ||
| } | ||
| } | ||
@@ -146,10 +146,7 @@ | ||
| throwIfAborted() | ||
| const abortController = new AbortController() | ||
| const signal = abortController.signal | ||
| const removeAbortCallback = abortCallbackList.add(() => { | ||
| abortController.abort() | ||
| }) | ||
| try { | ||
@@ -165,6 +162,23 @@ const value = await asyncCallback(signal) | ||
| const withSignalSync = (callback) => { | ||
| const abortController = new AbortController() | ||
| const signal = abortController.signal | ||
| const removeAbortCallback = abortCallbackList.add(() => { | ||
| abortController.abort() | ||
| }) | ||
| try { | ||
| const value = callback(signal) | ||
| removeAbortCallback() | ||
| return value | ||
| } catch (e) { | ||
| removeAbortCallback() | ||
| throw e | ||
| } | ||
| } | ||
| return { | ||
| // We don't have to expose the operationSignal | ||
| // it can be used to know if the operation is aborted so it mabe be handy | ||
| // but in that case we should rather prefer .aborted directly? | ||
| // We could almost hide the operationSignal | ||
| // But it can be handy for 2 things: | ||
| // - know if operation is aborted (operation.signal.aborted) | ||
| // - forward the operation.signal directly (not using "withSignal" or "withSignalSync") | ||
| signal: operationSignal, | ||
@@ -178,6 +192,10 @@ | ||
| withSignal, | ||
| gracefulStop, | ||
| withSignalSync, | ||
| clean, | ||
| } | ||
| } | ||
| const FROM_ABORT = { from: "abort" } | ||
| const FROM_CLEAN = { from: "clean" } | ||
| const cancelNoop = () => {} | ||
@@ -184,0 +202,0 @@ |
@@ -17,3 +17,3 @@ export const createCallbackList = () => { | ||
| CODE: "CALLBACK_DUPLICATION", | ||
| detail: `It's often the sign that code is executd more than once`, | ||
| detail: `Code is potentially executed more than it should`, | ||
| }) | ||
@@ -20,0 +20,0 @@ } else { |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
15526
7.36%319
8.14%1
-50%85
8.97%