diff options
author | Andreu Botella <andreu@andreubotella.com> | 2022-03-14 20:19:22 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-14 20:19:22 +0100 |
commit | 9f494dc405afc4b1b29fa4c813bd5751f26aaa36 (patch) | |
tree | 2757ee74fe3f5e9e134e1265bc71d87febc63d32 /ext/web/03_abort_signal.js | |
parent | 5eb0e4c2dfab4b83fbc69746d8e1143f78e9154e (diff) |
feat(ext/web): Add `AbortSignal.timeout()` (#13687)
Diffstat (limited to 'ext/web/03_abort_signal.js')
-rw-r--r-- | ext/web/03_abort_signal.js | 44 |
1 files changed, 44 insertions, 0 deletions
diff --git a/ext/web/03_abort_signal.js b/ext/web/03_abort_signal.js index 8b089d031..39de8d0fc 100644 --- a/ext/web/03_abort_signal.js +++ b/ext/web/03_abort_signal.js @@ -7,6 +7,7 @@ ((window) => { const webidl = window.__bootstrap.webidl; const { setIsTrusted, defineEventHandler } = window.__bootstrap.event; + const { listenerCount } = window.__bootstrap.eventTarget; const { Set, SetPrototypeAdd, @@ -14,6 +15,7 @@ Symbol, TypeError, } = window.__bootstrap.primordials; + const { setTimeout, refTimer, unrefTimer } = window.__bootstrap.timers; const add = Symbol("[[add]]"); const signalAbort = Symbol("[[signalAbort]]"); @@ -21,6 +23,7 @@ const abortReason = Symbol("[[abortReason]]"); const abortAlgos = Symbol("[[abortAlgos]]"); const signal = Symbol("[[signal]]"); + const timerId = Symbol("[[timerId]]"); const illegalConstructorKey = Symbol("illegalConstructorKey"); @@ -34,6 +37,27 @@ return signal; } + static timeout(millis) { + const prefix = "Failed to call 'AbortSignal.timeout'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + millis = webidl.converters["unsigned long long"](millis, { + enforceRange: true, + }); + + const signal = new AbortSignal(illegalConstructorKey); + signal[timerId] = setTimeout( + () => { + signal[timerId] = null; + signal[signalAbort]( + new DOMException("Signal timed out.", "TimeoutError"), + ); + }, + millis, + ); + unrefTimer(signal[timerId]); + return signal; + } + [add](algorithm) { if (this.aborted) { return; @@ -73,6 +97,7 @@ super(); this[abortReason] = undefined; this[abortAlgos] = null; + this[timerId] = null; this[webidl.brand] = webidl.brand; } @@ -92,6 +117,25 @@ throw this[abortReason]; } } + + // `addEventListener` and `removeEventListener` have to be overriden in + // order to have the timer block the event loop while there are listeners. + // `[add]` and `[remove]` don't ref and unref the timer because they can + // only be used by Deno internals, which use it to essentially cancel async + // ops which would block the event loop. + addEventListener(...args) { + super.addEventListener(...args); + if (this[timerId] !== null && listenerCount(this, "abort") > 0) { + refTimer(this[timerId]); + } + } + + removeEventListener(...args) { + super.removeEventListener(...args); + if (this[timerId] !== null && listenerCount(this, "abort") === 0) { + unrefTimer(this[timerId]); + } + } } defineEventHandler(AbortSignal.prototype, "abort"); |