| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent ab50ae6 commit 0876a29
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -88,7 +88,7 @@ class BroadcastImpl { | |||
| 88 | 88 | #consumers = new SafeSet(); | |
| 89 | 89 | #waiters = []; // Consumers with pending resolve (subset of #consumers) | |
| 90 | 90 | #ended = false; | |
| 91 | - #error = null; | ||
| 91 | + #error; | ||
| 92 | 92 | #cancelled = false; | |
| 93 | 93 | #options; | |
| 94 | 94 | #writer = null; | |
@@ -177,7 +177,9 @@ class BroadcastImpl { | |||
| 177 | 177 | __proto__: null, | |
| 178 | 178 | next() { | |
| 179 | 179 | if (state.detached) { | |
| 180 | - if (self.#error) return PromiseReject(self.#error); | ||
| 180 | + if (self.#error !== undefined) { | ||
| 181 | + return PromiseReject(self.#error); | ||
| 182 | + } | ||
| 181 | 183 | return kDone; | |
| 182 | 184 | } | |
| 183 | 185 | ||
@@ -194,7 +196,7 @@ class BroadcastImpl { | |||
| 194 | 196 | { __proto__: null, done: false, value: chunk }); | |
| 195 | 197 | } | |
| 196 | 198 | ||
| 197 | - if (self.#error) { | ||
| 199 | + if (self.#error !== undefined) { | ||
| 198 | 200 | state.detached = true; | |
| 199 | 201 | self.#deleteConsumer(state); | |
| 200 | 202 | return PromiseReject(self.#error); | |
@@ -344,7 +346,7 @@ class BroadcastImpl { | |||
| 344 | 346 | } | |
| 345 | 347 | ||
| 346 | 348 | [kAbort](reason) { | |
| 347 | - if (this.#ended || this.#error) return; | ||
| 349 | + if (this.#ended || this.#error !== undefined) return; | ||
| 348 | 350 | this.#error = reason; | |
| 349 | 351 | this.#ended = true; | |
| 350 | 352 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -73,7 +73,7 @@ class ShareImpl { | |||
| 73 | 73 | #consumers = new SafeSet(); | |
| 74 | 74 | #sourceIterator = null; | |
| 75 | 75 | #sourceExhausted = false; | |
| 76 | - #sourceError = null; | ||
| 76 | + #sourceError; | ||
| 77 | 77 | #cancelled = false; | |
| 78 | 78 | #pulling = false; | |
| 79 | 79 | #pullWaiters = []; | |
@@ -129,7 +129,7 @@ class ShareImpl { | |||
| 129 | 129 | __proto__: null, | |
| 130 | 130 | [SymbolAsyncIterator]() { | |
| 131 | 131 | const getNext = async () => { | |
| 132 | - if (self.#sourceError) { | ||
| 132 | + if (self.#sourceError !== undefined) { | ||
| 133 | 133 | state.detached = true; | |
| 134 | 134 | self.#consumers.delete(state); | |
| 135 | 135 | throw self.#sourceError; | |
@@ -141,7 +141,7 @@ class ShareImpl { | |||
| 141 | 141 | // cursor must re-pull rather than terminating prematurely. | |
| 142 | 142 | for (;;) { | |
| 143 | 143 | if (state.detached) { | |
| 144 | - if (self.#sourceError) throw self.#sourceError; | ||
| 144 | + if (self.#sourceError !== undefined) throw self.#sourceError; | ||
| 145 | 145 | return { __proto__: null, done: true, value: undefined }; | |
| 146 | 146 | } | |
| 147 | 147 | ||
@@ -167,7 +167,7 @@ class ShareImpl { | |||
| 167 | 167 | if (self.#sourceExhausted) { | |
| 168 | 168 | state.detached = true; | |
| 169 | 169 | self.#deleteConsumer(state); | |
| 170 | - if (self.#sourceError) throw self.#sourceError; | ||
| 170 | + if (self.#sourceError !== undefined) throw self.#sourceError; | ||
| 171 | 171 | return { __proto__: null, done: true, value: undefined }; | |
| 172 | 172 | } | |
| 173 | 173 | ||
@@ -176,7 +176,7 @@ class ShareImpl { | |||
| 176 | 176 | if (shouldBuffer === null) { | |
| 177 | 177 | state.detached = true; | |
| 178 | 178 | self.#deleteConsumer(state); | |
| 179 | - if (self.#sourceError) throw self.#sourceError; | ||
| 179 | + if (self.#sourceError !== undefined) throw self.#sourceError; | ||
| 180 | 180 | return { __proto__: null, done: true, value: undefined }; | |
| 181 | 181 | } | |
| 182 | 182 | ||
@@ -260,7 +260,9 @@ class ShareImpl { | |||
| 260 | 260 | ||
| 261 | 261 | async #waitForBufferSpace() { | |
| 262 | 262 | while (this.#bufferedBytes >= this.#options.budget) { | |
| 263 | - if (this.#cancelled || this.#sourceError || this.#sourceExhausted) { | ||
| 263 | + if (this.#cancelled || | ||
| 264 | + this.#sourceError !== undefined || | ||
| 265 | + this.#sourceExhausted) { | ||
| 264 | 266 | return this.#cancelled ? null : true; | |
| 265 | 267 | } | |
| 266 | 268 | ||
@@ -418,7 +420,7 @@ class SyncShareImpl { | |||
| 418 | 420 | #consumers = new SafeSet(); | |
| 419 | 421 | #sourceIterator = null; | |
| 420 | 422 | #sourceExhausted = false; | |
| 421 | - #sourceError = null; | ||
| 423 | + #sourceError; | ||
| 422 | 424 | #cancelled = false; | |
| 423 | 425 | #cachedMinCursor = 0; | |
| 424 | 426 | #cachedMinCursorConsumers = 0; | |
@@ -467,14 +469,14 @@ class SyncShareImpl { | |||
| 467 | 469 | return { | |
| 468 | 470 | __proto__: null, | |
| 469 | 471 | next() { | |
| 470 | - if (state.detached) { | ||
| 471 | - return { __proto__: null, done: true, value: undefined }; | ||
| 472 | - } | ||
| 473 | - if (self.#sourceError) { | ||
| 472 | + if (self.#sourceError !== undefined) { | ||
| 474 | 473 | state.detached = true; | |
| 475 | 474 | self.#deleteConsumer(state); | |
| 476 | 475 | throw self.#sourceError; | |
| 477 | 476 | } | |
| 477 | + if (state.detached) { | ||
| 478 | + return { __proto__: null, done: true, value: undefined }; | ||
| 479 | + } | ||
| 478 | 480 | if (self.#cancelled) { | |
| 479 | 481 | state.detached = true; | |
| 480 | 482 | self.#deleteConsumer(state); | |
@@ -535,7 +537,7 @@ class SyncShareImpl { | |||
| 535 | 537 | ||
| 536 | 538 | self.#pullFromSource(); | |
| 537 | 539 | ||
| 538 | - if (self.#sourceError) { | ||
| 540 | + if (self.#sourceError !== undefined) { | ||
| 539 | 541 | state.detached = true; | |
| 540 | 542 | self.#deleteConsumer(state); | |
| 541 | 543 | throw self.#sourceError; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -260,15 +260,15 @@ async function testWriterFailIdempotent() { | |||
| 260 | 260 | }, { message: 'fail!' }); | |
| 261 | 261 | } | |
| 262 | 262 | ||
| 263 | - // cancel() with falsy reason (0, "", false) should still treat as error | ||
| 264 | 263 | async function testCancelWithFalsyReason() { | |
| 265 | - const { broadcast: bc } = broadcast(); | ||
| 266 | - const consumer = bc.push(); | ||
| 267 | - const resultPromise = text(consumer).catch((err) => err); | ||
| 268 | - await new Promise((resolve) => setImmediate(resolve)); | ||
| 269 | - bc.cancel(0); | ||
| 270 | - const result = await resultPromise; | ||
| 271 | - assert.strictEqual(result, 0); | ||
| 264 | + for (const reason of [0, '', false, null]) { | ||
| 265 | + const { broadcast: bc } = broadcast(); | ||
| 266 | + const iterator = bc.push()[Symbol.asyncIterator](); | ||
| 267 | + | ||
| 268 | + bc.cancel(reason); | ||
| 269 | + | ||
| 270 | + await assert.rejects(iterator.next(), (error) => error === reason); | ||
| 271 | + } | ||
| 272 | 272 | } | |
| 273 | 273 | ||
| 274 | 274 | // Late-joining consumer should read from oldest buffered entry | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -132,6 +132,17 @@ async function testShareCancelWithReason() { | |||
| 132 | 132 | ); | |
| 133 | 133 | } | |
| 134 | 134 | ||
| 135 | + async function testShareCancelWithFalsyReason() { | ||
| 136 | + for (const reason of [0, '', false, null]) { | ||
| 137 | + const shared = share(from('data')); | ||
| 138 | + const iterator = shared.pull()[Symbol.asyncIterator](); | ||
| 139 | + | ||
| 140 | + shared.cancel(reason); | ||
| 141 | + | ||
| 142 | + await assert.rejects(iterator.next(), (error) => error === reason); | ||
| 143 | + } | ||
| 144 | + } | ||
| 145 | + | ||
| 135 | 146 | async function testShareAbortSignal() { | |
| 136 | 147 | const ac = new AbortController(); | |
| 137 | 148 | const reason = new Error('share aborted'); | |
@@ -357,6 +368,7 @@ Promise.all([ | |||
| 357 | 368 | testShareCancel(), | |
| 358 | 369 | testShareCancelMidIteration(), | |
| 359 | 370 | testShareCancelWithReason(), | |
| 371 | + testShareCancelWithFalsyReason(), | ||
| 360 | 372 | testShareAbortSignal(), | |
| 361 | 373 | testShareAbortSignalWhileSourcePullPending(), | |
| 362 | 374 | testSharePullAbortSignalRejectsPendingNext(), | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -87,37 +87,32 @@ function testShareSyncCancelMidIteration() { | |||
| 87 | 87 | } | |
| 88 | 88 | ||
| 89 | 89 | function testShareSyncCancelWithReason() { | |
| 90 | - // When cancel(reason) is called, a consumer that hasn't started | ||
| 91 | - // iterating is already detached, so it sees done:true (not the error). | ||
| 92 | - // But a consumer that is mid-iteration when another consumer cancels | ||
| 93 | - // with a reason will see the error on the next pull after cancel. | ||
| 94 | 90 | const enc = new TextEncoder(); | |
| 95 | 91 | function* gen() { | |
| 96 | 92 | yield [enc.encode('a')]; | |
| 97 | 93 | yield [enc.encode('b')]; | |
| 98 | - yield [enc.encode('c')]; | ||
| 99 | 94 | } | |
| 100 | 95 | const shared = shareSync(gen(), { budget: 16384 }); | |
| 101 | - const c1 = shared.pull(); | ||
| 102 | - const c2 = shared.pull(); | ||
| 96 | + const iterator1 = shared.pull()[Symbol.iterator](); | ||
| 97 | + const iterator2 = shared.pull()[Symbol.iterator](); | ||
| 98 | + const reason = new Error('sync cancel reason'); | ||
| 99 | + | ||
| 100 | + iterator1.next(); | ||
| 101 | + shared.cancel(reason); | ||
| 103 | 102 | ||
| 104 | - // c1 reads one item, then c2 cancels with a reason | ||
| 105 | - const iter1 = c1[Symbol.iterator](); | ||
| 106 | - const first = iter1.next(); | ||
| 107 | - assert.strictEqual(first.done, false); | ||
| 103 | + assert.throws(() => iterator1.next(), (error) => error === reason); | ||
| 104 | + assert.throws(() => iterator2.next(), (error) => error === reason); | ||
| 105 | + } | ||
| 108 | 106 | ||
| 109 | - shared.cancel(new Error('sync cancel reason')); | ||
| 107 | + function testShareSyncCancelWithFalsyReason() { | ||
| 108 | + for (const reason of [0, '', false, null]) { | ||
| 109 | + const shared = shareSync(fromSync('data')); | ||
| 110 | + const iterator = shared.pull()[Symbol.iterator](); | ||
| 110 | 111 | ||
| 111 | - // c1 was already iterating, it's now detached → done | ||
| 112 | - const next = iter1.next(); | ||
| 113 | - assert.strictEqual(next.done, true); | ||
| 112 | + shared.cancel(reason); | ||
| 114 | 113 | ||
| 115 | - // c2 never started, also detached → done (not error) | ||
| 116 | - const batches = []; | ||
| 117 | - for (const batch of c2) { | ||
| 118 | - batches.push(batch); | ||
| 114 | + assert.throws(() => iterator.next(), (error) => error === reason); | ||
| 119 | 115 | } | |
| 120 | - assert.strictEqual(batches.length, 0); | ||
| 121 | 116 | } | |
| 122 | 117 | ||
| 123 | 118 | // ============================================================================= | |
@@ -157,6 +152,7 @@ Promise.all([ | |||
| 157 | 152 | testShareSyncCancel(), | |
| 158 | 153 | testShareSyncCancelMidIteration(), | |
| 159 | 154 | testShareSyncCancelWithReason(), | |
| 155 | + testShareSyncCancelWithFalsyReason(), | ||
| 160 | 156 | testShareSyncSourceError(), | |
| 161 | 157 | testShareSyncStringSource(), | |
| 162 | 158 | ]).then(common.mustCall()); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments