11// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
22// This module is browser compatible.
33
4- import { createAbortError } from "./_util.ts" ;
5-
64/**
75 * Make a {@linkcode Promise} abortable with the given signal.
86 *
7+ * @throws {DOMException } If the signal is already aborted and `signal.reason`
8+ * is undefined. Otherwise, throws `signal.reason`.
99 * @typeParam T The type of the provided and returned promise.
1010 * @param p The promise to make abortable.
1111 * @param signal The signal to abort the promise with.
1212 * @returns A promise that can be aborted.
1313 *
14- * @example Usage
15- * ```ts no-eval
16- * import {
17- * abortable,
18- * delay,
19- * } from "@std/async";
14+ * @example Error-handling a timeout
15+ * ```ts
16+ * import { abortable, delay } from "@std/async";
17+ * import { assertRejects, assertEquals } from "@std/assert";
18+ *
19+ * const promise = delay(1_000);
20+ *
21+ * // Rejects with `DOMException` after 100 ms
22+ * await assertRejects(
23+ * () => abortable(promise, AbortSignal.timeout(100)),
24+ * DOMException,
25+ * "Signal timed out."
26+ * );
27+ * ```
28+ *
29+ * @example Error-handling an abort
30+ * ```ts
31+ * import { abortable, delay } from "@std/async";
32+ * import { assertRejects, assertEquals } from "@std/assert";
2033 *
21- * const p = delay(1000 );
22- * const c = new AbortController();
23- * setTimeout(() => c.abort(), 100 );
34+ * const promise = delay(1_000 );
35+ * const controller = new AbortController();
36+ * controller.abort(new Error("This is my reason") );
2437 *
25- * // Below throws `DOMException` after 100 ms
26- * await abortable(p, c.signal);
38+ * // Rejects with `DOMException` immediately
39+ * await assertRejects(
40+ * () => abortable(promise, controller.signal),
41+ * Error,
42+ * "This is my reason"
43+ * );
2744 * ```
2845 */
2946export function abortable < T > ( p : Promise < T > , signal : AbortSignal ) : Promise < T > ;
3047/**
3148 * Make an {@linkcode AsyncIterable} abortable with the given signal.
3249 *
50+ * @throws {DOMException } If the signal is already aborted and `signal.reason`
51+ * is undefined. Otherwise, throws `signal.reason`.
3352 * @typeParam T The type of the provided and returned async iterable.
3453 * @param p The async iterable to make abortable.
3554 * @param signal The signal to abort the promise with.
3655 * @returns An async iterable that can be aborted.
3756 *
38- * @example Usage
39- * ```ts no-eval
40- * import {
41- * abortable,
42- * delay,
43- * } from "@std/async";
57+ * @example Error-handling a timeout
58+ * ```ts
59+ * import { abortable, delay } from "@std/async";
60+ * import { assertRejects, assertEquals } from "@std/assert";
4461 *
45- * const p = async function* () {
62+ * const asyncIter = async function* () {
4663 * yield "Hello";
47- * await delay(1000 );
64+ * await delay(1_000 );
4865 * yield "World";
4966 * };
50- * const c = new AbortController();
51- * setTimeout(() => c.abort(), 100);
5267 *
53- * // Below throws `DOMException` after 100 ms
54- * // and items become `["Hello"]`
5568 * const items: string[] = [];
56- * for await (const item of abortable(p(), c.signal)) {
57- * items.push(item);
58- * }
69+ * // Below throws `DOMException` after 100 ms and items become `["Hello"]`
70+ * await assertRejects(
71+ * async () => {
72+ * for await (const item of abortable(asyncIter(), AbortSignal.timeout(100))) {
73+ * items.push(item);
74+ * }
75+ * },
76+ * DOMException,
77+ * "Signal timed out."
78+ * );
79+ * assertEquals(items, ["Hello"]);
80+ * ```
81+ *
82+ * @example Error-handling an abort
83+ * ```ts
84+ * import { abortable, delay } from "@std/async";
85+ * import { assertRejects, assertEquals } from "@std/assert";
86+ *
87+ * const asyncIter = async function* () {
88+ * yield "Hello";
89+ * await delay(1_000);
90+ * yield "World";
91+ * };
92+ * const controller = new AbortController();
93+ * controller.abort(new Error("This is my reason"));
94+ *
95+ * const items: string[] = [];
96+ * // Below throws `DOMException` immediately
97+ * await assertRejects(
98+ * async () => {
99+ * for await (const item of abortable(asyncIter(), controller.signal)) {
100+ * items.push(item);
101+ * }
102+ * },
103+ * Error,
104+ * "This is my reason"
105+ * );
106+ * assertEquals(items, []);
59107 * ```
60108 */
61109export function abortable < T > (
@@ -77,11 +125,9 @@ function abortablePromise<T>(
77125 p : Promise < T > ,
78126 signal : AbortSignal ,
79127) : Promise < T > {
80- if ( signal . aborted ) {
81- return Promise . reject ( createAbortError ( signal . reason ) ) ;
82- }
128+ if ( signal . aborted ) return Promise . reject ( signal . reason ) ;
83129 const { promise, reject } = Promise . withResolvers < never > ( ) ;
84- const abort = ( ) => reject ( createAbortError ( signal . reason ) ) ;
130+ const abort = ( ) => reject ( signal . reason ) ;
85131 signal . addEventListener ( "abort" , abort , { once : true } ) ;
86132 return Promise . race ( [ promise , p ] ) . finally ( ( ) => {
87133 signal . removeEventListener ( "abort" , abort ) ;
@@ -92,11 +138,9 @@ async function* abortableAsyncIterable<T>(
92138 p : AsyncIterable < T > ,
93139 signal : AbortSignal ,
94140) : AsyncGenerator < T > {
95- if ( signal . aborted ) {
96- throw createAbortError ( signal . reason ) ;
97- }
141+ signal . throwIfAborted ( ) ;
98142 const { promise, reject } = Promise . withResolvers < never > ( ) ;
99- const abort = ( ) => reject ( createAbortError ( signal . reason ) ) ;
143+ const abort = ( ) => reject ( signal . reason ) ;
100144 signal . addEventListener ( "abort" , abort , { once : true } ) ;
101145
102146 const it = p [ Symbol . asyncIterator ] ( ) ;
0 commit comments