diff --git a/index.bs b/index.bs index 8944441c0..e9acd02a7 100644 --- a/index.bs +++ b/index.bs @@ -18,6 +18,8 @@ spec:promises-guide; type:dfn;
urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT text: %Uint8Array%; url: #sec-typedarray-objects; type: constructor + text: %AsyncIteratorPrototype%; url: #sec-asynciteratorprototype; type: interface + text: AsyncIterator; url: #sec-asynciterator-interface; type: interface text: ArrayBuffer; url: #sec-arraybuffer-objects; type: interface text: DataView; url: #sec-dataview-objects; type: interface text: Number; url: #sec-ecmascript-language-types-number-type; type: interface @@ -399,11 +401,14 @@ like get locked() cancel(reason) - getReader() + getIterator({ preventCancel } = {}) + getReader({ mode } = {}) pipeThrough({ writable, readable }, { preventClose, preventAbort, preventCancel, signal } = {}) pipeTo(dest, { preventClose, preventAbort, preventCancel, signal } = {}) tee() + + [@@asyncIterator]({ preventCancel } = {}) }@@ -602,6 +607,23 @@ option. If
type
is set to unde
1. Return ! ReadableStreamCancel(*this*, _reason_).
+getIterator({ preventCancel } = {})
+
+
+ The getIterator
method returns an async iterator which can be used to consume the stream. The
+ {{ReadableStreamAsyncIteratorPrototype/return()}} method of this iterator object will, by default,
+ cancel the stream; it will also release the reader.
+
+
+
+ 1. If ! IsReadableStream(*this*) is *false*, throw a *TypeError* exception.
+ 1. Let _reader_ be ? AcquireReadableStreamDefaultReader(*this*).
+ 1. Let _iterator_ be ! ObjectCreate(`ReadableStreamAsyncIteratorPrototype`).
+ 1. Set _iterator_.[[asyncIteratorReader]] to _reader_.
+ 1. Set _iterator_.[[preventCancel]] to ! ToBoolean(_preventCancel_).
+ 1. Return _iterator_.
+
+
getReader({ mode } = {})
@@ -792,6 +814,82 @@ option. If type
is set to unde
+
+[@@asyncIterator]({ preventCancel } = {})
+
+
+ The @@asyncIterator
method is an alias of {{ReadableStream/getIterator()}}.
+
+
+The initial value of the @@asyncIterator
method is the same function object as the initial value of the
+{{ReadableStream/getIterator()}} method.
+
+ReadableStreamAsyncIteratorPrototype
+
+{{ReadableStreamAsyncIteratorPrototype}} is an ordinary object that is used by {{ReadableStream/getIterator()}} to
+construct the objects it returns. Instances of {{ReadableStreamAsyncIteratorPrototype}} implement the {{AsyncIterator}}
+abstract interface from the JavaScript specification. [[!ECMASCRIPT]]
+
+The {{ReadableStreamAsyncIteratorPrototype}} object must have its \[[Prototype]] internal slot set to
+{{%AsyncIteratorPrototype%}}.
+
+Internal slots
+
+Objects created by {{ReadableStream/getIterator()}}, using {{ReadableStreamAsyncIteratorPrototype}} as their
+prototype, are created with the internal slots described in the following table:
+
+
+
+
+ Internal Slot
+ Description (non-normative)
+
+
+
+ \[[asyncIteratorReader]]
+ A {{ReadableStreamDefaultReader}} instance
+
+
+ \[[preventCancel]]
+ A boolean value indicating if the stream will be canceled when the async iterator's {{ReadableStreamAsyncIteratorPrototype/return()}} method is called
+
+
+
+next()
+
+
+ 1. If ! IsReadableStreamAsyncIterator(*this*) is *false*, return a promise rejected with a *TypeError* exception.
+ 1. Let _reader_ be *this*.[[asyncIteratorReader]].
+ 1. If _reader_.[[ownerReadableStream]] is *undefined*, return a promise rejected with a *TypeError* exception.
+ 1. Return the result of transforming ! ReadableStreamDefaultReaderRead(_reader_) with a fulfillment handler
+ which takes the argument _result_ and performs the following steps:
+ 1. Assert: Type(_result_) is Object.
+ 1. Let _value_ be ? Get(_result_, `"value"`).
+ 1. Let _done_ be ? Get(_result_, `"done"`).
+ 1. Assert: Type(_done_) is Boolean.
+ 1. If _done_ is *true*, perform ! ReadableStreamReaderGenericRelease(_reader_).
+ 1. Return ! ReadableStreamCreateReadResult(_value_, _done_, *true*).
+
+
+return( value )
+
+
+ 1. If ! IsReadableStreamAsyncIterator(*this*) is *false*, return a promise rejected with a *TypeError* exception.
+ 1. Let _reader_ be *this*.[[asyncIteratorReader]].
+ 1. If _reader_.[[ownerReadableStream]] is *undefined*, return a promise rejected with a *TypeError* exception.
+ 1. If _reader_.[[readRequests]] is not empty, return a promise rejected with a *TypeError* exception.
+ 1. If *this*.[[preventCancel]] is *false*, then:
+ 1. Let _result_ be ! ReadableStreamReaderGenericCancel(_reader_, _value_).
+ 1. Perform ! ReadableStreamReaderGenericRelease(_reader_).
+ 1. Return the result of transforming _result_ by a fulfillment handler that returns !
+ ReadableStreamCreateReadResult(_value_, *true*, *true*).
+ 1. Perform ! ReadableStreamReaderGenericRelease(_reader_).
+ 1. Return a promise resolved with ! ReadableStreamCreateReadResult(_value_, *true*, *true*).
+
+
General readable stream abstract operations
The following abstract operations, unlike most in this specification, are meant to be generally useful by other
@@ -910,6 +1008,15 @@ readable stream is locked to a reader.
1. Return *true*.
+IsReadableStreamAsyncIterator ( x )
+
+
+ 1. If Type(_x_) is not Object, return *false*.
+ 1. If _x_ does not have a [[asyncIteratorReader]] internal slot, return *false*.
+ 1. Return *true*.
+
+
ReadableStreamTee ( stream,
cloneForBranch2 )
@@ -5985,6 +6092,7 @@ Forbes Lindesay,
Forrest Norvell,
Gary Blackwood,
Gorgi Kosev,
+Gus Caplan,
贺师俊 (hax),
Isaac Schlueter,
isonmad,
diff --git a/reference-implementation/.eslintrc.json b/reference-implementation/.eslintrc.json
index c09c1518a..f3486a456 100644
--- a/reference-implementation/.eslintrc.json
+++ b/reference-implementation/.eslintrc.json
@@ -160,7 +160,15 @@
"id-blacklist": "off",
"id-length": "off",
"id-match": "off",
- "indent": ["error", 2, { "SwitchCase": 1 }],
+ "indent": ["error", 2, {
+ "SwitchCase": 1,
+ "FunctionDeclaration": { "parameters": "first" },
+ "FunctionExpression": { "parameters": "first" },
+ "CallExpression": { "arguments": "first" },
+ "ArrayExpression": "first",
+ "ObjectExpression": "first",
+ "ImportDeclaration": "first"
+ }],
"jsx-quotes": "off",
"key-spacing": ["error", { "beforeColon": false, "afterColon": true, "mode": "strict" }],
"keyword-spacing": ["error", { "before": true, "after": true }],
diff --git a/reference-implementation/lib/readable-stream.js b/reference-implementation/lib/readable-stream.js
index 6187fc40b..78be77a57 100644
--- a/reference-implementation/lib/readable-stream.js
+++ b/reference-implementation/lib/readable-stream.js
@@ -129,7 +129,7 @@ class ReadableStream {
}
if (IsWritableStream(dest) === false) {
return Promise.reject(
- new TypeError('ReadableStream.prototype.pipeTo\'s first argument must be a WritableStream'));
+ new TypeError('ReadableStream.prototype.pipeTo\'s first argument must be a WritableStream'));
}
preventClose = Boolean(preventClose);
@@ -158,8 +158,72 @@ class ReadableStream {
const branches = ReadableStreamTee(this, false);
return createArrayFromList(branches);
}
+
+ getIterator({ preventCancel = false } = {}) {
+ if (IsReadableStream(this) === false) {
+ throw streamBrandCheckException('getIterator');
+ }
+ const reader = AcquireReadableStreamDefaultReader(this);
+ const iterator = Object.create(ReadableStreamAsyncIteratorPrototype);
+ iterator._asyncIteratorReader = reader;
+ iterator._preventCancel = Boolean(preventCancel);
+ return iterator;
+ }
}
+const AsyncIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf(async function* () {}).prototype);
+const ReadableStreamAsyncIteratorPrototype = Object.setPrototypeOf({
+ next() {
+ if (IsReadableStreamAsyncIterator(this) === false) {
+ return Promise.reject(streamAsyncIteratorBrandCheckException('next'));
+ }
+ const reader = this._asyncIteratorReader;
+ if (reader._ownerReadableStream === undefined) {
+ return Promise.reject(readerLockException('iterate'));
+ }
+ return ReadableStreamDefaultReaderRead(reader).then(result => {
+ assert(typeIsObject(result));
+ const value = result.value;
+ const done = result.done;
+ assert(typeof done === 'boolean');
+ if (done) {
+ ReadableStreamReaderGenericRelease(reader);
+ }
+ return ReadableStreamCreateReadResult(value, done, true);
+ });
+ },
+
+ return(value) {
+ if (IsReadableStreamAsyncIterator(this) === false) {
+ return Promise.reject(streamAsyncIteratorBrandCheckException('next'));
+ }
+ const reader = this._asyncIteratorReader;
+ if (reader._ownerReadableStream === undefined) {
+ return Promise.reject(readerLockException('finish iterating'));
+ }
+ if (reader._readRequests.length > 0) {
+ return Promise.reject(new TypeError(
+ 'Tried to release a reader lock when that reader has pending read() calls un-settled'));
+ }
+ if (this._preventCancel === false) {
+ const result = ReadableStreamReaderGenericCancel(reader, value);
+ ReadableStreamReaderGenericRelease(reader);
+ return result.then(() => ReadableStreamCreateReadResult(value, true, true));
+ }
+ ReadableStreamReaderGenericRelease(reader);
+ return Promise.resolve(ReadableStreamCreateReadResult(value, true, true));
+ }
+}, AsyncIteratorPrototype);
+Object.defineProperty(ReadableStreamAsyncIteratorPrototype, 'next', { enumerable: false });
+Object.defineProperty(ReadableStreamAsyncIteratorPrototype, 'return', { enumerable: false });
+
+Object.defineProperty(ReadableStream.prototype, Symbol.asyncIterator, {
+ value: ReadableStream.prototype.getIterator,
+ enumerable: false,
+ writable: true,
+ configurable: true
+});
+
module.exports = {
CreateReadableByteStream,
CreateReadableStream,
@@ -194,7 +258,7 @@ function CreateReadableStream(startAlgorithm, pullAlgorithm, cancelAlgorithm, hi
const controller = Object.create(ReadableStreamDefaultController.prototype);
SetUpReadableStreamDefaultController(
- stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm
+ stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm
);
return stream;
@@ -255,6 +319,18 @@ function IsReadableStreamLocked(stream) {
return true;
}
+function IsReadableStreamAsyncIterator(x) {
+ if (!typeIsObject(x)) {
+ return false;
+ }
+
+ if (!Object.prototype.hasOwnProperty.call(x, '_asyncIteratorReader')) {
+ return false;
+ }
+
+ return true;
+}
+
function ReadableStreamPipeTo(source, dest, preventClose, preventAbort, preventCancel, signal) {
assert(IsReadableStream(source) === true);
assert(IsWritableStream(dest) === true);
@@ -420,10 +496,9 @@ function ReadableStreamPipeTo(source, dest, preventClose, preventAbort, preventC
function doTheRest() {
action().then(
- () => finalize(originalIsError, originalError),
- newError => finalize(true, newError)
- )
- .catch(rethrowAssertionErrorRejection);
+ () => finalize(originalIsError, originalError),
+ newError => finalize(true, newError)
+ ).catch(rethrowAssertionErrorRejection);
}
}
@@ -931,12 +1006,12 @@ function ReadableStreamReaderGenericRelease(reader) {
if (reader._ownerReadableStream._state === 'readable') {
defaultReaderClosedPromiseReject(
- reader,
- new TypeError('Reader was released and can no longer be used to monitor the stream\'s closedness'));
+ reader,
+ new TypeError('Reader was released and can no longer be used to monitor the stream\'s closedness'));
} else {
defaultReaderClosedPromiseResetToRejected(
- reader,
- new TypeError('Reader was released and can no longer be used to monitor the stream\'s closedness'));
+ reader,
+ new TypeError('Reader was released and can no longer be used to monitor the stream\'s closedness'));
}
reader._closedPromise.catch(() => {});
@@ -1098,8 +1173,7 @@ function ReadableStreamDefaultControllerCallPullIfNeeded(controller) {
e => {
ReadableStreamDefaultControllerError(controller, e);
}
- )
- .catch(rethrowAssertionErrorRejection);
+ ).catch(rethrowAssertionErrorRejection);
return undefined;
}
@@ -1260,8 +1334,7 @@ function SetUpReadableStreamDefaultController(
r => {
ReadableStreamDefaultControllerError(controller, r);
}
- )
- .catch(rethrowAssertionErrorRejection);
+ ).catch(rethrowAssertionErrorRejection);
}
function SetUpReadableStreamDefaultControllerFromUnderlyingSource(stream, underlyingSource, highWaterMark,
@@ -1533,8 +1606,7 @@ function ReadableByteStreamControllerCallPullIfNeeded(controller) {
e => {
ReadableByteStreamControllerError(controller, e);
}
- )
- .catch(rethrowAssertionErrorRejection);
+ ).catch(rethrowAssertionErrorRejection);
return undefined;
}
@@ -1570,7 +1642,7 @@ function ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescripto
assert(bytesFilled % elementSize === 0);
return new pullIntoDescriptor.ctor(
- pullIntoDescriptor.buffer, pullIntoDescriptor.byteOffset, bytesFilled / elementSize);
+ pullIntoDescriptor.buffer, pullIntoDescriptor.byteOffset, bytesFilled / elementSize);
}
function ReadableByteStreamControllerEnqueueChunkToQueue(controller, buffer, byteOffset, byteLength) {
@@ -1994,19 +2066,18 @@ function SetUpReadableByteStreamController(stream, controller, startAlgorithm, p
const startResult = startAlgorithm();
Promise.resolve(startResult).then(
- () => {
- controller._started = true;
+ () => {
+ controller._started = true;
- assert(controller._pulling === false);
- assert(controller._pullAgain === false);
+ assert(controller._pulling === false);
+ assert(controller._pullAgain === false);
- ReadableByteStreamControllerCallPullIfNeeded(controller);
- },
- r => {
- ReadableByteStreamControllerError(controller, r);
- }
- )
- .catch(rethrowAssertionErrorRejection);
+ ReadableByteStreamControllerCallPullIfNeeded(controller);
+ },
+ r => {
+ ReadableByteStreamControllerError(controller, r);
+ }
+ ).catch(rethrowAssertionErrorRejection);
}
function SetUpReadableByteStreamControllerFromUnderlyingSource(stream, underlyingByteSource, highWaterMark) {
@@ -2063,6 +2134,10 @@ function streamBrandCheckException(name) {
return new TypeError(`ReadableStream.prototype.${name} can only be used on a ReadableStream`);
}
+function streamAsyncIteratorBrandCheckException(name) {
+ return new TypeError(`ReadableStreamAsyncIterator.${name} can only be used on a ReadableSteamAsyncIterator`);
+}
+
// Helper functions for the readers.
function readerLockException(name) {
diff --git a/reference-implementation/lib/transform-stream.js b/reference-implementation/lib/transform-stream.js
index 6c8608208..a5f14e90e 100644
--- a/reference-implementation/lib/transform-stream.js
+++ b/reference-implementation/lib/transform-stream.js
@@ -357,16 +357,15 @@ function TransformStreamDefaultSinkWriteAlgorithm(stream, chunk) {
if (stream._backpressure === true) {
const backpressureChangePromise = stream._backpressureChangePromise;
assert(backpressureChangePromise !== undefined);
- return backpressureChangePromise
- .then(() => {
- const writable = stream._writable;
- const state = writable._state;
- if (state === 'erroring') {
- throw writable._storedError;
- }
- assert(state === 'writable');
- return TransformStreamDefaultControllerPerformTransform(controller, chunk);
- });
+ return backpressureChangePromise.then(() => {
+ const writable = stream._writable;
+ const state = writable._state;
+ if (state === 'erroring') {
+ throw writable._storedError;
+ }
+ assert(state === 'writable');
+ return TransformStreamDefaultControllerPerformTransform(controller, chunk);
+ });
}
return TransformStreamDefaultControllerPerformTransform(controller, chunk);
diff --git a/reference-implementation/lib/writable-stream.js b/reference-implementation/lib/writable-stream.js
index 661c4cf31..ab3dc5c52 100644
--- a/reference-implementation/lib/writable-stream.js
+++ b/reference-implementation/lib/writable-stream.js
@@ -272,14 +272,14 @@ function WritableStreamFinishErroring(stream) {
const promise = stream._writableStreamController[AbortSteps](abortRequest._reason);
promise.then(
- () => {
- abortRequest._resolve();
- WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
- },
- reason => {
- abortRequest._reject(reason);
- WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
- });
+ () => {
+ abortRequest._resolve();
+ WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
+ },
+ reason => {
+ abortRequest._reject(reason);
+ WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
+ });
}
function WritableStreamFinishInFlightWrite(stream) {
@@ -769,18 +769,17 @@ function SetUpWritableStreamDefaultController(stream, controller, startAlgorithm
const startResult = startAlgorithm();
const startPromise = Promise.resolve(startResult);
startPromise.then(
- () => {
- assert(stream._state === 'writable' || stream._state === 'erroring');
- controller._started = true;
- WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
- },
- r => {
- assert(stream._state === 'writable' || stream._state === 'erroring');
- controller._started = true;
- WritableStreamDealWithRejection(stream, r);
- }
- )
- .catch(rethrowAssertionErrorRejection);
+ () => {
+ assert(stream._state === 'writable' || stream._state === 'erroring');
+ controller._started = true;
+ WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
+ },
+ r => {
+ assert(stream._state === 'writable' || stream._state === 'erroring');
+ controller._started = true;
+ WritableStreamDealWithRejection(stream, r);
+ }
+ ).catch(rethrowAssertionErrorRejection);
}
function SetUpWritableStreamDefaultControllerFromUnderlyingSink(stream, underlyingSink, highWaterMark, sizeAlgorithm) {
@@ -903,8 +902,7 @@ function WritableStreamDefaultControllerProcessClose(controller) {
reason => {
WritableStreamFinishInFlightCloseWithError(stream, reason);
}
- )
- .catch(rethrowAssertionErrorRejection);
+ ).catch(rethrowAssertionErrorRejection);
}
function WritableStreamDefaultControllerProcessWrite(controller, chunk) {
@@ -935,8 +933,7 @@ function WritableStreamDefaultControllerProcessWrite(controller, chunk) {
}
WritableStreamFinishInFlightWriteWithError(stream, reason);
}
- )
- .catch(rethrowAssertionErrorRejection);
+ ).catch(rethrowAssertionErrorRejection);
}
function WritableStreamDefaultControllerGetBackpressure(controller) {
diff --git a/reference-implementation/package.json b/reference-implementation/package.json
index 19dd1e6a2..bda74d15a 100644
--- a/reference-implementation/package.json
+++ b/reference-implementation/package.json
@@ -14,7 +14,7 @@
"better-assert": "^1.0.2",
"browserify": "^16.2.3",
"debug": "^4.1.0",
- "eslint": "^3.2.2",
+ "eslint": "^5.12.1",
"minimatch": "^3.0.4",
"nyc": "^13.0.1",
"opener": "^1.5.1",
diff --git a/reference-implementation/web-platform-tests b/reference-implementation/web-platform-tests
index 4606e75ca..de6f8fcf9 160000
--- a/reference-implementation/web-platform-tests
+++ b/reference-implementation/web-platform-tests
@@ -1 +1 @@
-Subproject commit 4606e75ca8cd69830223f02e0fbd46fc160f431f
+Subproject commit de6f8fcf9b87e80811e9267a886cf891f6f864e0