@jsenv/abort
Advanced tools
Comparing version 4.2.4 to 4.3.0
{ | ||
"name": "@jsenv/abort", | ||
"version": "4.2.4", | ||
"version": "4.3.0", | ||
"description": "Help to write code compatible with abort signals", | ||
@@ -8,4 +8,4 @@ "license": "MIT", | ||
"type": "git", | ||
"url": "https://github.com/jsenv/jsenv-core", | ||
"directory": "packages/abort" | ||
"url": "https://github.com/jsenv/core", | ||
"directory": "packages/independent/abort" | ||
}, | ||
@@ -35,4 +35,3 @@ "publishConfig": { | ||
"test": "node --conditions=development ./scripts/test.mjs" | ||
}, | ||
"dependencies": {} | ||
} | ||
} |
@@ -17,10 +17,10 @@ # Abort [![npm package](https://img.shields.io/npm/v/@jsenv/abort.svg?logo=npm&label=package)](https://www.npmjs.com/package/@jsenv/abort) | ||
```js | ||
import { customFetch } from "./fetch_custom.mjs" | ||
import { customFetch } from "./fetch_custom.mjs"; | ||
const abortController = new AbortController() | ||
const signal = abortController.signal | ||
const abortController = new AbortController(); | ||
const signal = abortController.signal; | ||
process.on("warning", () => { | ||
abortController.abort() | ||
}) | ||
await customFetch("http://example.com", { signal }) | ||
abortController.abort(); | ||
}); | ||
await customFetch("http://example.com", { signal }); | ||
``` | ||
@@ -27,0 +27,0 @@ |
232
src/abort.js
@@ -5,12 +5,12 @@ /* | ||
import { raceCallbacks } from "./callback_race.js" | ||
import { createCallbackListNotifiedOnce } from "./callback_list_once.js" | ||
import { raceCallbacks } from "./callback_race.js"; | ||
import { createCallbackListNotifiedOnce } from "./callback_list_once.js"; | ||
export const Abort = { | ||
isAbortError: (error) => { | ||
return error && error.name === "AbortError" | ||
return error && error.name === "AbortError"; | ||
}, | ||
startOperation: () => { | ||
return createOperation() | ||
return createOperation(); | ||
}, | ||
@@ -20,14 +20,14 @@ | ||
if (signal.aborted) { | ||
const error = new Error(`The operation was aborted`) | ||
error.name = "AbortError" | ||
error.type = "aborted" | ||
throw error | ||
const error = new Error(`The operation was aborted`); | ||
error.name = "AbortError"; | ||
error.type = "aborted"; | ||
throw error; | ||
} | ||
}, | ||
} | ||
}; | ||
const createOperation = () => { | ||
const operationAbortController = new AbortController() | ||
const operationAbortController = new AbortController(); | ||
// const abortOperation = (value) => abortController.abort(value) | ||
const operationSignal = operationAbortController.signal | ||
const operationSignal = operationAbortController.signal; | ||
@@ -39,21 +39,21 @@ // abortCallbackList is used to ignore the max listeners warning from Node.js | ||
// uses abortCallbackList to know when something is aborted | ||
const abortCallbackList = createCallbackListNotifiedOnce() | ||
const endCallbackList = createCallbackListNotifiedOnce() | ||
const abortCallbackList = createCallbackListNotifiedOnce(); | ||
const endCallbackList = createCallbackListNotifiedOnce(); | ||
let isAbortAfterEnd = false | ||
let isAbortAfterEnd = false; | ||
operationSignal.onabort = () => { | ||
operationSignal.onabort = null | ||
operationSignal.onabort = null; | ||
const allAbortCallbacksPromise = Promise.all(abortCallbackList.notify()) | ||
const allAbortCallbacksPromise = Promise.all(abortCallbackList.notify()); | ||
if (!isAbortAfterEnd) { | ||
addEndCallback(async () => { | ||
await allAbortCallbacksPromise | ||
}) | ||
await allAbortCallbacksPromise; | ||
}); | ||
} | ||
} | ||
}; | ||
const throwIfAborted = () => { | ||
Abort.throwIfAborted(operationSignal) | ||
} | ||
Abort.throwIfAborted(operationSignal); | ||
}; | ||
@@ -74,13 +74,13 @@ // add a callback called on abort | ||
if (operationSignal.aborted) { | ||
return addEndCallback(callback) | ||
return addEndCallback(callback); | ||
} | ||
return abortCallbackList.add(callback) | ||
} | ||
return abortCallbackList.add(callback); | ||
}; | ||
const addEndCallback = (callback) => { | ||
return endCallbackList.add(callback) | ||
} | ||
return endCallbackList.add(callback); | ||
}; | ||
const end = async ({ abortAfterEnd = false } = {}) => { | ||
await Promise.all(endCallbackList.notify()) | ||
await Promise.all(endCallbackList.notify()); | ||
@@ -97,7 +97,7 @@ // "abortAfterEnd" can be handy to ensure "abort" callbacks | ||
if (!operationSignal.aborted) { | ||
isAbortAfterEnd = true | ||
operationAbortController.abort() | ||
isAbortAfterEnd = true; | ||
operationAbortController.abort(); | ||
} | ||
} | ||
} | ||
}; | ||
@@ -109,24 +109,24 @@ const addAbortSignal = ( | ||
const applyAbortEffects = () => { | ||
const onAbortCallback = onAbort | ||
onAbort = callbackNoop | ||
onAbortCallback() | ||
} | ||
const onAbortCallback = onAbort; | ||
onAbort = callbackNoop; | ||
onAbortCallback(); | ||
}; | ||
const applyRemoveEffects = () => { | ||
const onRemoveCallback = onRemove | ||
onRemove = callbackNoop | ||
onAbort = callbackNoop | ||
onRemoveCallback() | ||
} | ||
const onRemoveCallback = onRemove; | ||
onRemove = callbackNoop; | ||
onAbort = callbackNoop; | ||
onRemoveCallback(); | ||
}; | ||
if (operationSignal.aborted) { | ||
applyAbortEffects() | ||
applyRemoveEffects() | ||
return callbackNoop | ||
applyAbortEffects(); | ||
applyRemoveEffects(); | ||
return callbackNoop; | ||
} | ||
if (signal.aborted) { | ||
operationAbortController.abort() | ||
applyAbortEffects() | ||
applyRemoveEffects() | ||
return callbackNoop | ||
operationAbortController.abort(); | ||
applyAbortEffects(); | ||
applyRemoveEffects(); | ||
return callbackNoop; | ||
} | ||
@@ -137,9 +137,9 @@ | ||
operation_abort: (cb) => { | ||
return addAbortCallback(cb) | ||
return addAbortCallback(cb); | ||
}, | ||
operation_end: (cb) => { | ||
return addEndCallback(cb) | ||
return addEndCallback(cb); | ||
}, | ||
child_abort: (cb) => { | ||
return addEventListener(signal, "abort", cb) | ||
return addEventListener(signal, "abort", cb); | ||
}, | ||
@@ -157,4 +157,4 @@ }, | ||
operation_abort: () => { | ||
applyAbortEffects() | ||
applyRemoveEffects() | ||
applyAbortEffects(); | ||
applyRemoveEffects(); | ||
}, | ||
@@ -166,18 +166,18 @@ operation_end: () => { | ||
// - call any custom cancel function | ||
applyRemoveEffects() | ||
applyRemoveEffects(); | ||
}, | ||
child_abort: () => { | ||
applyAbortEffects() | ||
operationAbortController.abort() | ||
applyAbortEffects(); | ||
operationAbortController.abort(); | ||
}, | ||
} | ||
raceEffects[winner.name](winner.value) | ||
}; | ||
raceEffects[winner.name](winner.value); | ||
}, | ||
) | ||
); | ||
return () => { | ||
cancelRace() | ||
applyRemoveEffects() | ||
} | ||
} | ||
cancelRace(); | ||
applyRemoveEffects(); | ||
}; | ||
}; | ||
@@ -189,27 +189,27 @@ const addAbortSource = (abortSourceCallback) => { | ||
remove: callbackNoop, | ||
} | ||
const abortSourceController = new AbortController() | ||
const abortSourceSignal = abortSourceController.signal | ||
abortSource.signal = abortSourceSignal | ||
}; | ||
const abortSourceController = new AbortController(); | ||
const abortSourceSignal = abortSourceController.signal; | ||
abortSource.signal = abortSourceSignal; | ||
if (operationSignal.aborted) { | ||
return abortSource | ||
return abortSource; | ||
} | ||
const returnValue = abortSourceCallback((value) => { | ||
abortSourceController.abort(value) | ||
}) | ||
abortSourceController.abort(value); | ||
}); | ||
const removeAbortSignal = addAbortSignal(abortSourceSignal, { | ||
onRemove: () => { | ||
if (typeof returnValue === "function") { | ||
returnValue() | ||
returnValue(); | ||
} | ||
abortSource.cleaned = true | ||
abortSource.cleaned = true; | ||
}, | ||
}) | ||
abortSource.remove = removeAbortSignal | ||
return abortSource | ||
} | ||
}); | ||
abortSource.remove = removeAbortSignal; | ||
return abortSource; | ||
}; | ||
const timeout = (ms) => { | ||
return addAbortSource((abort) => { | ||
const timeoutId = setTimeout(abort, ms) | ||
const timeoutId = setTimeout(abort, ms); | ||
// an abort source return value is called when: | ||
@@ -219,43 +219,61 @@ // - operation is aborted (by an other source) | ||
return () => { | ||
clearTimeout(timeoutId) | ||
} | ||
}) | ||
} | ||
clearTimeout(timeoutId); | ||
}; | ||
}); | ||
}; | ||
const wait = (ms) => { | ||
return new Promise((resolve) => { | ||
const timeoutId = setTimeout(() => { | ||
removeAbortCallback(); | ||
resolve(); | ||
}, ms); | ||
const removeAbortCallback = addAbortCallback(() => { | ||
clearTimeout(timeoutId); | ||
}); | ||
}); | ||
}; | ||
const withSignal = async (asyncCallback) => { | ||
const abortController = new AbortController() | ||
const signal = abortController.signal | ||
const abortController = new AbortController(); | ||
const signal = abortController.signal; | ||
const removeAbortSignal = addAbortSignal(signal, { | ||
onAbort: () => { | ||
abortController.abort() | ||
abortController.abort(); | ||
}, | ||
}) | ||
}); | ||
try { | ||
const value = await asyncCallback(signal) | ||
removeAbortSignal() | ||
return value | ||
const value = await asyncCallback(signal); | ||
removeAbortSignal(); | ||
return value; | ||
} catch (e) { | ||
removeAbortSignal() | ||
throw e | ||
removeAbortSignal(); | ||
throw e; | ||
} | ||
} | ||
}; | ||
const withSignalSync = (callback) => { | ||
const abortController = new AbortController() | ||
const signal = abortController.signal | ||
const abortController = new AbortController(); | ||
const signal = abortController.signal; | ||
const removeAbortSignal = addAbortSignal(signal, { | ||
onAbort: () => { | ||
abortController.abort() | ||
abortController.abort(); | ||
}, | ||
}) | ||
}); | ||
try { | ||
const value = callback(signal) | ||
removeAbortSignal() | ||
return value | ||
const value = callback(signal); | ||
removeAbortSignal(); | ||
return value; | ||
} catch (e) { | ||
removeAbortSignal() | ||
throw e | ||
removeAbortSignal(); | ||
throw e; | ||
} | ||
} | ||
}; | ||
const fork = () => { | ||
const forkedOperation = createOperation(); | ||
forkedOperation.addAbortSignal(operationSignal); | ||
return forkedOperation; | ||
}; | ||
return { | ||
@@ -272,3 +290,5 @@ // We could almost hide the operationSignal | ||
addAbortSource, | ||
fork, | ||
timeout, | ||
wait, | ||
withSignal, | ||
@@ -278,12 +298,12 @@ withSignalSync, | ||
end, | ||
} | ||
} | ||
}; | ||
}; | ||
const callbackNoop = () => {} | ||
const callbackNoop = () => {}; | ||
const addEventListener = (target, eventName, cb) => { | ||
target.addEventListener(eventName, cb) | ||
target.addEventListener(eventName, cb); | ||
return () => { | ||
target.removeEventListener(eventName, cb) | ||
} | ||
} | ||
target.removeEventListener(eventName, cb); | ||
}; | ||
}; |
export const createCallbackListNotifiedOnce = () => { | ||
let callbacks = [] | ||
let status = "waiting" | ||
let currentCallbackIndex = -1 | ||
let callbacks = []; | ||
let status = "waiting"; | ||
let currentCallbackIndex = -1; | ||
const callbackListOnce = {} | ||
const callbackListOnce = {}; | ||
const add = (callback) => { | ||
if (status !== "waiting") { | ||
emitUnexpectedActionWarning({ action: "add", status }) | ||
return removeNoop | ||
emitUnexpectedActionWarning({ action: "add", status }); | ||
return removeNoop; | ||
} | ||
if (typeof callback !== "function") { | ||
throw new Error(`callback must be a function, got ${callback}`) | ||
throw new Error(`callback must be a function, got ${callback}`); | ||
} | ||
@@ -20,10 +20,10 @@ | ||
const existingCallback = callbacks.find((callbackCandidate) => { | ||
return callbackCandidate === callback | ||
}) | ||
return callbackCandidate === callback; | ||
}); | ||
if (existingCallback) { | ||
emitCallbackDuplicationWarning() | ||
return removeNoop | ||
emitCallbackDuplicationWarning(); | ||
return removeNoop; | ||
} | ||
callbacks.push(callback) | ||
callbacks.push(callback); | ||
return () => { | ||
@@ -33,8 +33,8 @@ if (status === "notified") { | ||
// as the callbacks array is frozen to null | ||
return | ||
return; | ||
} | ||
const index = callbacks.indexOf(callback) | ||
const index = callbacks.indexOf(callback); | ||
if (index === -1) { | ||
return | ||
return; | ||
} | ||
@@ -48,3 +48,3 @@ | ||
// would be skipped | ||
return | ||
return; | ||
} | ||
@@ -56,32 +56,32 @@ | ||
callbacks.splice(index, 1) | ||
} | ||
} | ||
callbacks.splice(index, 1); | ||
}; | ||
}; | ||
const notify = (param) => { | ||
if (status !== "waiting") { | ||
emitUnexpectedActionWarning({ action: "call", status }) | ||
return [] | ||
emitUnexpectedActionWarning({ action: "call", status }); | ||
return []; | ||
} | ||
status = "looping" | ||
status = "looping"; | ||
const values = callbacks.map((callback, index) => { | ||
currentCallbackIndex = index | ||
return callback(param) | ||
}) | ||
callbackListOnce.notified = true | ||
status = "notified" | ||
currentCallbackIndex = index; | ||
return callback(param); | ||
}); | ||
callbackListOnce.notified = true; | ||
status = "notified"; | ||
// we reset callbacks to null after looping | ||
// so that it's possible to remove during the loop | ||
callbacks = null | ||
currentCallbackIndex = -1 | ||
callbacks = null; | ||
currentCallbackIndex = -1; | ||
return values | ||
} | ||
return values; | ||
}; | ||
callbackListOnce.notified = false | ||
callbackListOnce.add = add | ||
callbackListOnce.notify = notify | ||
callbackListOnce.notified = false; | ||
callbackListOnce.add = add; | ||
callbackListOnce.notify = notify; | ||
return callbackListOnce | ||
} | ||
return callbackListOnce; | ||
}; | ||
@@ -96,9 +96,9 @@ const emitUnexpectedActionWarning = ({ action, status }) => { | ||
}, | ||
) | ||
); | ||
} else { | ||
console.warn( | ||
`"${action}" should not happen when callback list is ${status}`, | ||
) | ||
); | ||
} | ||
} | ||
}; | ||
@@ -110,8 +110,8 @@ const emitCallbackDuplicationWarning = () => { | ||
detail: `Code is potentially executed more than it should`, | ||
}) | ||
}); | ||
} else { | ||
console.warn(`Trying to add same callback twice`) | ||
console.warn(`Trying to add same callback twice`); | ||
} | ||
} | ||
}; | ||
const removeNoop = () => {} | ||
const removeNoop = () => {}; |
export const createCallbackList = () => { | ||
let callbacks = [] | ||
let callbacks = []; | ||
const add = (callback) => { | ||
if (typeof callback !== "function") { | ||
throw new Error(`callback must be a function, got ${callback}`) | ||
throw new Error(`callback must be a function, got ${callback}`); | ||
} | ||
@@ -11,26 +11,26 @@ | ||
const existingCallback = callbacks.find((callbackCandidate) => { | ||
return callbackCandidate === callback | ||
}) | ||
return callbackCandidate === callback; | ||
}); | ||
if (existingCallback) { | ||
emitCallbackDuplicationWarning() | ||
return removeNoop | ||
emitCallbackDuplicationWarning(); | ||
return removeNoop; | ||
} | ||
callbacks.push(callback) | ||
callbacks.push(callback); | ||
return () => { | ||
const index = callbacks.indexOf(callback) | ||
const index = callbacks.indexOf(callback); | ||
if (index === -1) { | ||
return | ||
return; | ||
} | ||
callbacks.splice(index, 1) | ||
} | ||
} | ||
callbacks.splice(index, 1); | ||
}; | ||
}; | ||
const notify = (param) => { | ||
const values = callbacks.slice().map((callback) => { | ||
return callback(param) | ||
}) | ||
return values | ||
} | ||
return callback(param); | ||
}); | ||
return values; | ||
}; | ||
@@ -40,4 +40,4 @@ return { | ||
notify, | ||
} | ||
} | ||
}; | ||
}; | ||
@@ -49,8 +49,8 @@ const emitCallbackDuplicationWarning = () => { | ||
detail: `Code is potentially executed more than it should`, | ||
}) | ||
}); | ||
} else { | ||
console.warn(`Trying to add same callback twice`) | ||
console.warn(`Trying to add same callback twice`); | ||
} | ||
} | ||
}; | ||
const removeNoop = () => {} | ||
const removeNoop = () => {}; |
@@ -6,39 +6,39 @@ /* | ||
export const raceCallbacks = (raceDescription, winnerCallback) => { | ||
let cleanCallbacks = [] | ||
let status = "racing" | ||
let cleanCallbacks = []; | ||
let status = "racing"; | ||
const clean = () => { | ||
cleanCallbacks.forEach((clean) => { | ||
clean() | ||
}) | ||
cleanCallbacks = null | ||
} | ||
clean(); | ||
}); | ||
cleanCallbacks = null; | ||
}; | ||
const cancel = () => { | ||
if (status !== "racing") { | ||
return | ||
return; | ||
} | ||
status = "cancelled" | ||
clean() | ||
} | ||
status = "cancelled"; | ||
clean(); | ||
}; | ||
Object.keys(raceDescription).forEach((candidateName) => { | ||
const register = raceDescription[candidateName] | ||
const register = raceDescription[candidateName]; | ||
const returnValue = register((data) => { | ||
if (status !== "racing") { | ||
return | ||
return; | ||
} | ||
status = "done" | ||
clean() | ||
status = "done"; | ||
clean(); | ||
winnerCallback({ | ||
name: candidateName, | ||
data, | ||
}) | ||
}) | ||
}); | ||
}); | ||
if (typeof returnValue === "function") { | ||
cleanCallbacks.push(returnValue) | ||
cleanCallbacks.push(returnValue); | ||
} | ||
}) | ||
}); | ||
return cancel | ||
} | ||
return cancel; | ||
}; |
@@ -8,3 +8,3 @@ ## raceCallbacks | ||
```js | ||
import { raceCallbacks } from "somewhere" | ||
import { raceCallbacks } from "somewhere"; | ||
@@ -14,18 +14,18 @@ raceCallbacks( | ||
timeout: (cb) => { | ||
const timeout = setTimeout(cb, 1000) | ||
const timeout = setTimeout(cb, 1000); | ||
return () => { | ||
clearTimeout(timeout) | ||
} | ||
clearTimeout(timeout); | ||
}; | ||
}, | ||
error: () => { | ||
something.on("error", cb) | ||
something.on("error", cb); | ||
return () => { | ||
return something.removeListener("error", cb) | ||
} | ||
return something.removeListener("error", cb); | ||
}; | ||
}, | ||
end: () => { | ||
something.on("end", cb) | ||
something.on("end", cb); | ||
return () => { | ||
return something.removeListener("end", cb) | ||
} | ||
return something.removeListener("end", cb); | ||
}; | ||
}, | ||
@@ -38,6 +38,6 @@ }, | ||
end: (value) => console.log("end", value), | ||
} | ||
raceEffects[winner.name](winner.data) | ||
}; | ||
raceEffects[winner.name](winner.data); | ||
}, | ||
) | ||
); | ||
``` |
@@ -1,7 +0,7 @@ | ||
export { Abort } from "./abort.js" | ||
export { createCallbackList } from "./callback_list.js" | ||
export { createCallbackListNotifiedOnce } from "./callback_list_once.js" | ||
export { raceCallbacks } from "./callback_race.js" | ||
export { Abort } from "./abort.js"; | ||
export { createCallbackList } from "./callback_list.js"; | ||
export { createCallbackListNotifiedOnce } from "./callback_list_once.js"; | ||
export { raceCallbacks } from "./callback_race.js"; | ||
// exports specific to Node.js | ||
export { raceProcessTeardownEvents } from "./process_teardown_events.js" | ||
export { raceProcessTeardownEvents } from "./process_teardown_events.js"; |
@@ -1,2 +0,2 @@ | ||
import { raceCallbacks } from "./callback_race.js" | ||
import { raceCallbacks } from "./callback_race.js"; | ||
@@ -13,48 +13,48 @@ export const raceProcessTeardownEvents = (processTeardownEvents, callback) => { | ||
callback, | ||
) | ||
} | ||
); | ||
}; | ||
const SIGHUP_CALLBACK = { | ||
SIGHUP: (cb) => { | ||
process.on("SIGHUP", cb) | ||
process.on("SIGHUP", cb); | ||
return () => { | ||
process.removeListener("SIGHUP", cb) | ||
} | ||
process.removeListener("SIGHUP", cb); | ||
}; | ||
}, | ||
} | ||
}; | ||
const SIGTERM_CALLBACK = { | ||
SIGTERM: (cb) => { | ||
process.on("SIGTERM", cb) | ||
process.on("SIGTERM", cb); | ||
return () => { | ||
process.removeListener("SIGTERM", cb) | ||
} | ||
process.removeListener("SIGTERM", cb); | ||
}; | ||
}, | ||
} | ||
}; | ||
const BEFORE_EXIT_CALLBACK = { | ||
beforeExit: (cb) => { | ||
process.on("beforeExit", cb) | ||
process.on("beforeExit", cb); | ||
return () => { | ||
process.removeListener("beforeExit", cb) | ||
} | ||
process.removeListener("beforeExit", cb); | ||
}; | ||
}, | ||
} | ||
}; | ||
const EXIT_CALLBACK = { | ||
exit: (cb) => { | ||
process.on("exit", cb) | ||
process.on("exit", cb); | ||
return () => { | ||
process.removeListener("exit", cb) | ||
} | ||
process.removeListener("exit", cb); | ||
}; | ||
}, | ||
} | ||
}; | ||
const SIGINT_CALLBACK = { | ||
SIGINT: (cb) => { | ||
process.on("SIGINT", cb) | ||
process.on("SIGINT", cb); | ||
return () => { | ||
process.removeListener("SIGINT", cb) | ||
} | ||
process.removeListener("SIGINT", cb); | ||
}; | ||
}, | ||
} | ||
}; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
19047
504