1. 소개
이 섹션은 비규범적입니다.
웹 플랫폼의 많은 부분은 스트리밍 데이터에 기반합니다. 즉, 데이터가 점진적으로 생성, 처리, 소비되며, 모든 데이터를 한 번에 메모리로 읽지 않습니다. 스트림스 표준은 이러한 스트리밍 데이터 생성을 위한 공통 API 집합을 제공하며, 읽기 가능한 스트림, 쓰기 가능한 스트림, 변환 스트림에 구현되어 있습니다.
이 API들은 저수준 I/O 프리미티브에 효율적으로 매핑되도록 설계되었으며, 필요에 따라 바이트 스트림을 위한 특화도 있습니다. 여러 스트림을 파이프 체인으로 쉽게 합성하거나 리더와 라이터를 통해 직접 사용할 수 있습니다. 또한 역압 및 큐잉을 자동으로 제공합니다.
이 표준은 웹 플랫폼의 다른 부분에서 스트리밍 데이터를 노출할 수 있도록 하는 기본 스트림 프리미티브를 제공합니다. 예를 들어, [FETCH]는 Response
본문을 ReadableStream
인스턴스로 노출합니다. 더 일반적으로, 플랫폼에는 스트림으로 표현될 수 있는 스트리밍 추상화가 많습니다. 멀티미디어 스트림, 파일 스트림, 글로벌 간 통신 등은 데이터를 한 번에
버퍼링하지 않고 점진적으로 처리할 수 있다는 점에서 이점을 얻습니다. 이러한 스트림을 개발자에게 노출할 수 있는 기반을 제공함으로써, 스트림스 표준은 다음과 같은 활용 사례를 가능하게
합니다:
-
비디오 효과: 읽기 가능한 비디오 스트림을 변환 스트림에 파이프하여 실시간으로 효과를 적용합니다.
-
압축 해제: 파일 스트림을 변환 스트림에 파이프하여 .tgz 아카이브에서 선택적으로 파일을 압축 해제하고, 사용자가 이미지 갤러리를 스크롤할 때
img
요소로 변환합니다. -
이미지 디코딩: HTTP 응답 스트림을 변환 스트림에 파이프하여 바이트를 비트맵 데이터로 디코딩한 뒤, 추가 변환을 통해 비트맵을 PNG로 변환합니다. 서비스 워커의
fetch
후크에 설치하면 새로운 이미지 포맷을 투명하게 폴리필 할 수 있습니다. [SERVICE-WORKERS]
웹 개발자는 여기서 설명하는 API를 사용해 플랫폼과 동일한 API로 자신만의 스트림을 만들 수 있습니다. 다른 개발자는 플랫폼에서 제공하는 스트림과 라이브러리에서 제공하는 스트림을 투명하게 합성할 수 있습니다. 이처럼, 여기서 설명하는 API는 모든 스트림을 위한 통합 추상화를 제공하며, 공유되고 합성 가능한 인터페이스를 중심으로 생태계가 성장하도록 장려합니다.
2. 모델
청크란 스트림에 기록되거나 읽혀지는 단일 데이터 조각을
의미합니다. 타입은 무엇이든 될 수 있으며, 스트림에는 서로 다른 타입의 청크도 포함될 수 있습니다. 청크는 특정 스트림에 대해 가장 작은 데이터 단위가 아닐 수 있습니다. 예를 들어, 바이트
스트림은 단일 바이트가 아닌 16KiB 크기의 Uint8Array
청크로 구성될 수 있습니다.
2.1. 읽기 가능한 스트림
읽기 가능한 스트림은 데이터를 읽을 수 있는
소스를 나타냅니다. 즉, 데이터가
밖으로 흘러나오는 스트림입니다. 구체적으로, 읽기 가능한 스트림은 ReadableStream
클래스의 인스턴스입니다.
읽기 가능한 스트림은 임의의 동작으로 생성될 수 있지만, 대부분은 기초 소스라 불리는 저수준 I/O 소스를 감쌉니다. 기초 소스에는 푸시 소스와 풀 소스 두 가지 타입이 있습니다.
푸시 소스는 리스너가 있든 없든 데이터를 지속적으로 보냅니다. 데이터 흐름을 일시중지하거나 재개할 수 있는 메커니즘을 제공하기도 합니다. 푸시 소스의 예로 TCP 소켓이 있으며, OS 레벨에서 지속적으로 데이터가 푸시되고, TCP 윈도우 크기를 조절하여 속도를 제어할 수 있습니다.
풀 소스는 데이터를 요청해야만 제공합니다. 데이터가 OS의 메모리 버퍼에 있을 경우 동기적으로 제공될 수 있고, 디스크에서 읽어야 한다면 비동기적으로 제공될 수 있습니다. 풀 소스의 예로 파일 핸들이 있으며, 특정 위치로 이동하여 원하는 양만큼 읽습니다.
읽기 가능한 스트림은 두 소스를 하나의 통합 인터페이스로 감쌀 수 있도록 설계되었습니다. 웹 개발자가 생성하는 스트림의 경우, 소스의 구현 세부사항은 특정 메서드와 속성을 가진 객체로 ReadableStream()
생성자에 전달됩니다.
청크는 스트림의 기초 소스에 의해 스트림에 인큐됩니다. 그런 다음, 스트림의 퍼블릭
인터페이스(특히 읽기 가능한 스트림 리더를 통해 getReader()
메서드로 획득 가능)를 사용해 청크를 하나씩 읽을 수 있습니다.
퍼블릭 인터페이스를 통해 읽기 가능한 스트림에서 데이터를 읽는 코드를 소비자라고 합니다.
소비자는 스트림 취소도 할 수 있습니다. cancel()
메서드를
사용하며, 이는 소비자가 스트림에 더 이상 관심이 없음을 나타내고, 스트림을 즉시 닫아 대기 중인 청크를 모두 버리고, 기초 소스의 취소 메커니즘을 실행합니다.
소비자는 스트림 tee도 할 수 있습니다. tee()
메서드를 사용하면 스트림을 잠금하여 직접 사용할 수는 없게 되지만, 독립적으로 소비할 수 있는 두 개의
새로운 스트림(분기)을 생성합니다.
바이트를 표현하는 스트림의 경우, 바이트 복사를 최소화하여 효율적으로 처리할 수 있도록 확장된 읽기 가능한 스트림이 제공됩니다. 이런 스트림의 기초 소스를 기초 바이트 소스라고 합니다. 기초 소스가 기초 바이트 소스인
읽기 가능한 스트림은 종종 읽기 가능한 바이트
스트림이라고 불립니다. 읽기 가능한 바이트 스트림의 소비자는 BYOB 리더를 스트림의 getReader()
메서드를 통해 획득할 수 있습니다.
2.2. 쓰기 가능한 스트림
쓰기 가능한 스트림은 데이터를 기록할 수 있는
목적지를 나타냅니다. 즉, 데이터가 안으로 들어가는 스트림입니다. 구체적으로, 쓰기 가능한 스트림은 WritableStream
클래스의 인스턴스입니다.
읽기 가능한 스트림과 유사하게, 대부분의 쓰기 가능한 스트림은 기초 싱크라 불리는 저수준 I/O 싱크를 감쌉니다. 쓰기 가능한 스트림은 이후 쓰기를 큐에 저장하고, 하나씩 기초 싱크에 전달함으로써 일부 복잡성을 추상화합니다.
청크는 스트림의 퍼블릭 인터페이스를 통해 기록되며, 하나씩 기초 싱크로 전달됩니다. 웹 개발자가 만든
스트림의 경우, 싱크의 구현 세부사항은 특정 메서드를 가진 객체로 WritableStream()
생성자에 전달됩니다.
퍼블릭 인터페이스를 사용해 쓰기 가능한 스트림에 데이터를 기록하는 코드를 생산자라고 합니다.
생산자는 스트림 중단도 할 수 있습니다. abort()
메서드를 사용하며,
생산자가 문제를 인식해 추가 기록을 중단해야 한다고 판단한 경우 사용합니다. 이는 기초 싱크의 신호 없이도 스트림을 오류 상태로 만들고, 내부 큐에 있는 모든 기록을 폐기합니다.
2.3. 변환 스트림
변환 스트림은 두 개의 스트림(한쪽은 쓰기 가능한 스트림으로 쓰기 측이라 하며, 다른 한쪽은 읽기 가능한 스트림으로 읽기 측이라 함)으로 이루어집니다. 특정 변환 스트림에 따라, 쓰기 측에 데이터를 기록하면 읽기 측에서 새로운 데이터가 제공됩니다.
구체적으로, writable
속성과 readable
속성을 가진 객체라면 변환 스트림으로 사용할 수 있습니다. 하지만 표준 TransformStream
클래스는 두 스트림을 올바르게 연결된 쌍으로 쉽게 생성할 수 있도록 도와줍니다. 이 클래스는 변환기를 감싸며, 특정 변환 알고리즘을 정의합니다. 웹 개발자가 만든 스트림의 경우, 변환기의 구현 세부사항은 특정 메서드와 속성을 가진 객체로 TransformStream()
생성자에 전달됩니다. 다른 명세에서는 GenericTransformStream
믹스인을 사용해 동일한 writable
/readable
속성 쌍과 다른 맞춤 API를 결합한 클래스를 만들 수 있습니다.
아이덴티티 변환 스트림은
쓰기 측에 기록된 모든 청크를 읽기 측으로 변환 없이 전달하는 특별한 변환
스트림입니다. 이는 다양한 시나리오에서 유용하게 사용될 수 있습니다. 기본적으로, TransformStream
생성자는 변환기 객체에 transform()
메서드가 없으면 아이덴티티 변환 스트림을 생성합니다.
변환 스트림의 예시는 다음과 같습니다:
-
GZIP 압축기: 압축되지 않은 바이트를 기록하면 압축된 바이트를 읽을 수 있음
-
비디오 디코더: 인코딩된 바이트를 기록하면 압축 해제된 비디오 프레임을 읽을 수 있음
-
텍스트 디코더: 바이트를 기록하면 문자열을 읽을 수 있음
-
CSV-JSON 변환기: CSV 파일의 한 줄을 나타내는 문자열을 기록하면 해당하는 JavaScript 객체를 읽을 수 있음
2.4. 파이프 체인과 역압
스트림은 주로 서로 파이핑하여 사용합니다. 읽기 가능한
스트림은 pipeTo()
메서드를 통해 쓰기 가능한 스트림에 직접 파이프할 수 있으며, pipeThrough()
메서드를 사용해 하나 이상의 변환 스트림을 거쳐 파이프할 수도 있습니다.
이와 같이 파이프된 스트림 집합을 파이프 체인이라고 합니다. 파이프 체인에서 원본 소스는 체인의 첫 번째 읽기 가능한 스트림의 기초 소스이고, 최종 싱크는 체인의 마지막 쓰기 가능한 스트림의 기초 싱크입니다.
파이프 체인이 구성되면, 청크(chunk)의 흐름 속도에 대한 신호가 체인을 따라 전달됩니다. 체인 내 어느 단계에서든 청크를 아직 받아들일 수 없는 경우, 신호가 파이프 체인을 역방향으로 전달되어 결국 원본 소스에게 청크 생산 속도를 늦추라는 신호가 전달됩니다. 이렇게 체인이 청크를 처리할 수 있는 속도에 맞춰 원본 소스의 흐름을 조정하는 과정을 역압이라고 합니다.
구체적으로, 원본 소스는 controller.desiredSize
또는 byteController.desiredSize
값을 받고, 데이터 흐름 속도를 이에 맞춰 조정할 수 있습니다. 이 값은 최종 싱크의 writer.desiredSize
에서 파생되며, 최종 싱크가 청크를 기록할 때마다 갱신됩니다. 체인을 구성하는 pipeTo()
메서드는 이러한 정보가 파이프 체인을 따라 역방향으로 자동으로
전달되도록 보장합니다.
읽기 가능한 스트림 tee 시, 두 분기에서 발생하는 역압 신호가 집계되어, 어느 분기도 읽히지 않으면 원본 스트림의 기초 소스로 역압 신호가 전달됩니다.
파이핑은 읽기 가능한 스트림과 쓰기 가능한 스트림을 잠금 상태로 만들어 파이프 작업이 진행되는 동안 스트림을 조작할 수 없게 합니다. 이를 통해 구현은 기초 소스에서 기초 싱크로 데이터를 직접 전달하는 등, 중간 큐를 우회하는 중요한 최적화를 수행할 수 있습니다.
2.5. 내부 큐 및 큐잉 전략
읽기 가능한 스트림과 쓰기 가능한 스트림 모두 유사한 목적으로 내부 큐를 유지합니다. 읽기 가능한 스트림의 경우 내부 큐에는 청크가 기초 소스에 의해 인큐되지만, 아직 소비자에 의해 읽히지 않은 상태로 남아 있습니다. 쓰기 가능한 스트림의 경우 내부 큐에는 생산자가 스트림에 기록했지만, 아직 기초 싱크에 의해 처리 및 승인되지 않은 청크가 포함됩니다.
큐잉 전략은 스트림이 내부 큐 상태를 바탕으로 역압을 어떻게 신호할지 결정하는 객체입니다. 큐잉 전략은 각 청크에 크기를 할당하고, 큐 내 모든 청크의 합계와 상수치(high water mark)라 불리는 특정 수치와 비교합니다. 상수치에서 총 크기를 뺀 값이 스트림 내부 큐를 채우기 위한 원하는 크기를 결정하는 데 사용됩니다.
읽기 가능한 스트림의 경우, 기초 소스는 이 원하는 크기를 역압 신호로 사용하여 청크 생성 속도를 늦추며, 원하는 크기가 0 이상으로 유지되도록 합니다. 쓰기 가능한 스트림의 경우, 생산자는 원하는 크기가 음수가 되지 않도록 기록을 자제할 수 있습니다.
구체적으로 웹 개발자가 만든 스트림의 큐잉 전략은 highWaterMark
속성을 가진 JavaScript 객체입니다. 바이트 스트림의 경우 highWaterMark
단위는 항상 바이트입니다. 그 외 스트림의 기본 단위는 청크지만, 전략 객체에
size()
함수를 포함해 각 청크의 크기를 반환하도록 할 수 있습니다. 이를 통해 highWaterMark
를 임의의 부동소수점 단위로 지정할 수 있습니다.
JavaScript에서는
처럼 직접 작성하거나,
내장 CountQueuingStrategy
클래스를 사용해
와
같이 구현할 수 있습니다.
2.6. 잠금
읽기 가능한 스트림 리더(reader)는 읽기 가능한 스트림에서 청크를 직접 읽을 수 있게 해주는 객체입니다. 리더가 없으면 소비자는 읽기 가능한 스트림에 대해 스트림 취소나 파이핑과 같은 고수준 작업만 수행할 수 있습니다. 리더는 스트림의 getReader()
메서드를 통해 획득합니다.
읽기 가능한 바이트 스트림은 두
가지 리더를 제공합니다: 기본 리더와 BYOB 리더입니다. BYOB("사용자 버퍼 직접 제공") 리더는 개발자가 제공한 버퍼에 직접 읽어 복사 횟수를 최소화합니다. 바이트가 아닌 읽기
가능한 스트림은 기본 리더만 제공합니다. 기본 리더는 ReadableStreamDefaultReader
클래스의 인스턴스이고, BYOB 리더는 ReadableStreamBYOBReader
클래스의 인스턴스입니다.
마찬가지로, 쓰기 가능한 스트림 라이터(writer)는 쓰기 가능한 스트림에 청크를 직접 기록할 수 있게 해주는 객체입니다. 라이터가 없으면 생산자는 스트림 중단이나 파이핑과 같은 고수준 작업만 수행할 수 있습니다. 라이터는 WritableStreamDefaultWriter
클래스의 인스턴스로 표현됩니다.
내부적으로는 이러한 고수준 작업도 실제로는 리더나 라이터를 사용합니다.
읽기 가능한 스트림이나 쓰기 가능한 스트림에는 동시에 하나의 리더 또는 라이터만 존재할 수 있습니다. 이 경우 스트림은 잠금 상태이며, 리더 또는 라이터는 활성 상태입니다. 이 상태는 readableStream.locked
또는 writableStream.locked
속성을 통해 확인할 수 있습니다.
리더 또는 라이터는 잠금 해제도 할 수 있으며, 이 경우 더 이상 활성 상태가 아니게 되고, 새로운 리더 또는 라이터를 획득할 수 있게
됩니다. 이는 defaultReader.releaseLock()
,
byobReader.releaseLock()
,
또는 writer.releaseLock()
메서드를 통해 수행합니다.
3. 관례
이 명세는 Infra 표준에 의존합니다. [INFRA]
이 명세는 내부 알고리즘에 대해 JavaScript 명세의 추상 연산(abstract operation) 개념을 사용합니다. 여기에는 반환값을 완료 레코드(completion record)로 처리하고, ! 및 ? 접두사를 사용해 완료 레코드를 해제하는 방식이 포함됩니다. [ECMASCRIPT]
이 명세는 또한 JavaScript 명세의 내부 슬롯(internal slot) 개념과 표기법을 사용합니다. (단, 내부 슬롯은 JavaScript 객체가 아닌 Web IDL 플랫폼 객체에 있습니다.)
이러한 외부 JavaScript 명세 관례의 사용 배경은 주로 역사적 이유입니다. 여러분이 자체 웹 명세를 작성할 때는 이러한 예시를 따르지 않기를 권장합니다.
이 명세에서 모든 숫자는 64비트 IEEE 754 부동소수점(double-precision) 값(예: JavaScript의 Number 타입 또는 Web IDL unrestricted double
타입)로 표현되며, 이들에 대한 모든 산술 연산은 해당 값에 대한 표준 방식으로 수행되어야 합니다. 이는 § 8.1 크기별 큐에서 설명한
데이터 구조에 특히 중요합니다. [IEEE-754]
4. 읽기 가능한 스트림
4.1. 읽기 가능한 스트림 사용하기
readableStream. pipeTo( writableStream) . then(() => console. log( "모든 데이터가 성공적으로 기록되었습니다!" )) . catch ( e=> console. error( "문제가 발생했습니다!" , e));
readableStream. pipeTo( new WritableStream({ write( chunk) { console. log( "청크 수신" , chunk); }, close() { console. log( "모든 데이터가 성공적으로 읽혔습니다!" ); }, abort( e) { console. error( "문제가 발생했습니다!" , e); } }));
read()
메서드로 순차적으로 청크를 직접 읽을 수도 있습니다. 예를 들어, 아래 코드는 스트림에서 다음 청크가 있으면 로그로 출력합니다:
const reader= readableStream. getReader(); reader. read(). then( ({ value, done}) => { if ( done) { console. log( "스트림이 이미 닫혔습니다!" ); } else { console. log( value); } }, e=> console. error( "스트림이 오류 상태가 되어 더 이상 읽을 수 없습니다!" , e) );
이런 수동적 스트림 읽기 방식은 파이핑과 tee 등 제공되는 고수준 연산을 넘어, 스트림을 활용한 새로운 고수준 연산을 만드는 라이브러리 저자에게 주로 유용합니다.
const reader= readableStream. getReader({ mode: "byob" }); let startingAB= new ArrayBuffer( 1024 ); const buffer= await readInto( startingAB); console. log( "처음 1024 바이트: " , buffer); async function readInto( buffer) { let offset= 0 ; while ( offset< buffer. byteLength) { const { value: view, done} = await reader. read( new Uint8Array( buffer, offset, buffer. byteLength- offset)); buffer= view. buffer; if ( done) { break ; } offset+= view. byteLength; } return buffer; }
여기서 중요한 점은 최종 buffer
값이 startingAB
와 다르지만, (그리고 모든 중간 버퍼도) 동일한 메모리 할당을 공유한다는
점입니다. 각 단계에서 버퍼는 transfer되어 새로운
ArrayBuffer
객체로 전달됩니다. view
는 Uint8Array
를
읽어서 반환값에서 구조 분해된 객체이며, ArrayBuffer
객체가 buffer
속성, 바이트가 기록된 오프셋이 byteOffset
속성, 기록된 바이트 수가 byteLength
속성으로 들어갑니다.
이 예시는 교육적 목적이 크며, 실제로는 min
옵션과 read()
를 사용하면 원하는 바이트 수를 더 쉽고 직접적으로 읽을 수 있습니다:
const reader= readableStream. getReader({ mode: "byob" }); const { value: view, done} = await reader. read( new Uint8Array( 1024 ), { min: 1024 }); console. log( "처음 1024 바이트: " , view);
4.2. ReadableStream
클래스
ReadableStream
클래스는 일반 읽기 가능한 스트림 개념의
구체적인 인스턴스입니다. 어떤 청크 타입에도 적응할 수 있으며, 기초 소스에서 공급되었으나 아직
소비자에 의해 읽히지 않은 데이터를 추적하는 내부 큐를 유지합니다.
4.2.1. 인터페이스 정의
ReadableStream
클래스의 Web IDL 정의는 다음과 같습니다:
[Exposed=*,Transferable ]interface {
ReadableStream constructor (optional object ,
underlyingSource optional QueuingStrategy = {});
strategy static ReadableStream from (any );
asyncIterable readonly attribute boolean locked ;Promise <undefined >cancel (optional any );
reason ReadableStreamReader getReader (optional ReadableStreamGetReaderOptions = {});
options ReadableStream pipeThrough (ReadableWritablePair ,
transform optional StreamPipeOptions = {});
options Promise <undefined >pipeTo (WritableStream ,
destination optional StreamPipeOptions = {});
options sequence <ReadableStream >tee ();async_iterable <any >(optional ReadableStreamIteratorOptions = {}); };
options typedef (ReadableStreamDefaultReader or ReadableStreamBYOBReader );
ReadableStreamReader enum {
ReadableStreamReaderMode };
"byob" dictionary {
ReadableStreamGetReaderOptions ReadableStreamReaderMode ; };
mode dictionary {
ReadableStreamIteratorOptions boolean =
preventCancel false ; };dictionary {
ReadableWritablePair required ReadableStream ;
readable required WritableStream ; };
writable dictionary {
StreamPipeOptions boolean =
preventClose false ;boolean =
preventAbort false ;boolean =
preventCancel false ;AbortSignal ; };
signal
4.2.2. 내부 슬롯
ReadableStream
인스턴스는 아래 표에 설명된 내부 슬롯과 함께 생성됩니다:
내부 슬롯 | 설명 (비규범적) |
---|---|
[[controller]] | ReadableStreamDefaultController
또는
ReadableByteStreamController
로, 이 스트림의 상태와 큐를 제어할 수 있도록 생성된 객체
|
[[Detached]] | 스트림이 전송될 때 true로 설정되는 불리언 플래그 |
[[disturbed]] | 스트림이 읽히거나 취소될 때 true로 설정되는 불리언 플래그 |
[[reader]] | ReadableStreamDefaultReader
또는 ReadableStreamBYOBReader
인스턴스로, 스트림이 리더에 잠긴 경우 설정되고, 그렇지 않으면
undefined
|
[[state]] | 스트림의 현재 상태를 나타내는 문자열로, 내부적으로 사용됨. 값은 "readable ",
"closed ", "errored " 중 하나
|
[[storedError]] | 스트림이 실패한 원인을 나타내는 값으로, 오류 상태의 스트림에서 작업 시 예외 또는 실패 사유로 반환됨 |
4.2.3. 기초 소스 API
ReadableStream()
생성자는 첫 번째 인자로 기초 소스를
나타내는 JavaScript 객체를 받습니다. 이러한 객체는 다음 속성 중 하나 이상을 포함할 수 있습니다:
dictionary {
UnderlyingSource UnderlyingSourceStartCallback start ;UnderlyingSourcePullCallback pull ;UnderlyingSourceCancelCallback cancel ;ReadableStreamType type ; [EnforceRange ]unsigned long long autoAllocateChunkSize ; };typedef (ReadableStreamDefaultController or ReadableByteStreamController );
ReadableStreamController callback =
UnderlyingSourceStartCallback any (ReadableStreamController );
controller callback =
UnderlyingSourcePullCallback Promise <undefined > (ReadableStreamController );
controller callback =
UnderlyingSourceCancelCallback Promise <undefined > (optional any );
reason enum {
ReadableStreamType "bytes" };
start(controller)
, 타입 UnderlyingSourceStartCallback-
ReadableStream
이 생성될 때 즉시 호출되는 함수입니다.보통 푸시 소스를 적응시키기 위해 관련 이벤트 리스너를 설정하거나(§ 10.1 기초 푸시 소스를 가진 읽기 가능한 스트림(역압 없음) 예시 참고), 풀 소스에 접근 권한을 획득하기 위해 사용합니다(§ 10.4 기초 풀 소스를 가진 읽기 가능한 스트림 참고).
이 설정 과정이 비동기적이라면 성공 또는 실패 신호를 위해 프로미스를 반환할 수 있습니다. 프로미스가 거부(reject)되면 스트림이 오류 상태가 됩니다. 예외가 발생하면
ReadableStream()
생성자가 예외를 다시 던집니다. pull(controller)
, 타입 UnderlyingSourcePullCallback-
스트림의 내부 큐가 가득 차 있지 않을 때(즉, 큐의 원하는 크기(desired size)가 양수일 때) 호출되는 함수입니다. 보통 큐가 상수치(high water mark)에 도달할 때까지 반복적으로 호출됩니다(즉, desired size가 0 이하가 될 때까지).
푸시 소스의 경우, § 10.2 기초 푸시 소스와 역압 지원이 있는 읽기 가능한 스트림처럼 일시중지된 데이터 흐름을 재개하는 데 사용할 수 있습니다. 풀 소스의 경우, 새로운 청크를 획득해 스트림에 인큐하는 데 사용됩니다(§ 10.4 기초 풀 소스를 가진 읽기 가능한 스트림 참고).
이 함수는
start()
가 성공적으로 완료되어야만 호출됩니다. 또한, 적어도 하나의 청크를 인큐하거나 BYOB 요청을 처리하는 경우에만 반복적으로 호출되며, 아무 작업도 하지 않는pull()
구현은 반복적으로 호출되지 않습니다.함수가 프로미스를 반환하면, 해당 프로미스가 완료될 때까지 다시 호출되지 않습니다. (프로미스가 거부되면 스트림이 오류 상태가 됩니다.) 이는 주로 풀 소스에서 사용되며, 반환된 프로미스가 새로운 청크 획득 과정을 나타냅니다. 예외를 던지면 거부된 프로미스를 반환한 것과 동일하게 처리됩니다.
cancel(reason)
, 타입 UnderlyingSourceCancelCallback-
소비자가
stream.cancel()
또는reader.cancel()
등을 통해 스트림을 취소할 때 호출되는 함수입니다. 인자로 소비자가 해당 메서드에 전달한 값이 그대로 전달됩니다.읽기 가능한 스트림은 파이프 과정(파이핑) 중 특정 조건에서 추가로 취소될 수 있습니다. 자세한 내용은
pipeTo()
메서드 정의를 참고하세요.모든 스트림에서, 이 함수는 일반적으로 기초 리소스에 대한 접근 권한을 해제하는 데 사용됩니다(§ 10.1 기초 푸시 소스를 가진 읽기 가능한 스트림(역압 없음) 참고).
종료 과정이 비동기적이라면 성공 또는 실패 신호를 위해 프로미스를 반환할 수 있습니다. 결과는 호출된
cancel()
메서드의 반환값을 통해 전달됩니다. 예외를 던지면 거부된 프로미스를 반환한 것과 동일하게 처리됩니다.취소 과정이 실패하더라도 스트림은 여전히 닫히며, 오류 상태가 되지 않습니다. 소비자가 스트림에 대한 관심을 잃어 취소를 요청한 이후에는, 취소 실패는 소비자의 관점에서 더 이상 중요하지 않기 때문입니다. 실패 정보는 해당 메서드의 직접 호출자에게만 전달됩니다.
이는
close
및abort
옵션이 실패하면WritableStream
이 오류 상태가 되는 기초 싱크의 동작과 다릅니다. 이러한 동작은 생산자가 요청한 특정 작업이 실패했음을 나타냅니다. type
(바이트 스트림만 해당), 타입 ReadableStreamType-
"
bytes
"로 설정하면, 생성된ReadableStream
이 읽기 가능한 바이트 스트림임을 신호합니다. 이 경우getReader()
메서드를 통해 BYOB 리더를 반환할 수 있습니다. 또한start()
및pull()
메서드에 전달되는 controller 인자에도 영향을 줍니다(아래 참조).읽기 가능한 바이트 스트림 설정 및 다른 컨트롤러 인터페이스 사용 예시는 § 10.3 기초 푸시 소스를 가진 읽기 가능한 바이트 스트림(역압 없음)을 참고하세요.
"
bytes
" 또는 undefined 이외의 값을 설정하면ReadableStream()
생성자가 예외를 던집니다. autoAllocateChunkSize
(바이트 스트림만 해당), 타입 unsigned long long-
양의 정수로 설정하면 구현이 기초 소스 코드가 쓸 수 있는 버퍼를 자동으로 할당하도록 합니다. 이 경우 소비자가 기본 리더를 사용할 때, 스트림 구현은 지정된 크기의
ArrayBuffer
를 자동으로 할당하여controller.byobRequest
가 항상 존재하도록 합니다. 이는 소비자가 BYOB 리더를 사용하는 것과 유사합니다.이는 기본 리더를 사용하는 소비자를 처리하는 데 필요한 코드 양을 줄이는 데 주로 사용됩니다. § 10.3 기초 푸시 소스를 가진 읽기 가능한 바이트 스트림(역압 없음)의 auto-allocation 없는 예와 § 10.5 기초 풀 소스를 가진 읽기 가능한 바이트 스트림의 auto-allocation 있는 예를 비교해보세요.
start()
와
pull()
메서드에 전달되는 controller 인자의 타입은 type
옵션의 값에 따라 다릅니다. type
을 undefined(생략 포함)로 설정하면 controller는 ReadableStreamDefaultController
입니다.
"bytes
"로
설정하면 controller는 ReadableByteStreamController
가
됩니다.
4.2.4. 생성자, 메서드, 속성
stream = new
ReadableStream
(underlyingSource[, strategy])-
주어진 기초 소스를 래핑하는 새로운
ReadableStream
을 생성합니다. underlyingSource 인자에 대한 자세한 내용은 § 4.2.3 기초 소스 API를 참고하세요.strategy 인자는 스트림의 큐잉 전략을 나타내며, § 7.1 큐잉 전략 API에서 설명합니다. 제공하지 않는 경우, 기본 동작은
CountQueuingStrategy
와 high water mark가 1인 것과 동일하게 동작합니다. stream =
ReadableStream.from
(asyncIterable)-
주어진 iterable 또는 async iterable을 감싸는 새로운
ReadableStream
을 생성합니다.이 메서드는 읽기 가능한 스트림으로 다양한 객체(예: 배열, 비동기 generator, Node.js readable stream)를 쉽게 적응시킬 수 있습니다.
isLocked = stream.
locked
-
해당 읽기 가능한 스트림이 리더에 잠겨 있는지 여부를 반환합니다.
await stream.
cancel
([ reason ])-
소비자가 스트림에 더 이상 관심이 없음을 신호하여 스트림을 취소합니다. 제공된 reason 인자는 기초 소스의
cancel()
메서드에 전달되며, 실제로 사용할지 여부는 구현에 따라 다릅니다.반환된 프로미스는 스트림이 정상적으로 종료되면 fulfill되고, 기초 소스에서 오류가 발생하면 reject됩니다. 또한 스트림이 현재 잠금 상태이면
TypeError
로 reject(스트림 취소 시도 없이)됩니다. reader = stream.
getReader
()-
ReadableStreamDefaultReader
를 생성하고 스트림을 새 리더에 잠금 상태로 만듭니다. 스트림이 잠긴 동안에는 이 리더가 잠금 해제될 때까지 다른 리더를 획득할 수 없습니다.이 기능은 스트림 전체를 소비하고자 하는 추상화를 만들 때 특히 유용합니다. 스트림에 대해 리더를 획득하면 다른 사람이 읽기 작업이나 취소를 끼워넣을 수 없어, 자신의 추상화가 방해받지 않습니다.
reader = stream.
getReader
({mode
: "byob
" })-
ReadableStreamBYOBReader
를 생성하고 스트림을 새 리더에 잠금 상태로 만듭니다.이 호출은 인자 없는 버전과 동작은 같지만, 읽기 가능한 바이트 스트림(즉, BYOB 읽기를 지원하도록 생성된 스트림)에만 작동합니다. 반환된 BYOB 리더는 개발자가 제공한 버퍼에 청크를 직접 읽을 수 있어, 버퍼 할당을 더욱 정밀하게 제어할 수 있습니다.
readable = stream.
pipeThrough
({writable
,readable
}[, {preventClose
,preventAbort
,preventCancel
,signal
}])-
이 읽기 가능한 스트림을 변환 스트림(또는
{ writable, readable }
쌍)으로 파이프하는 편리하고 체이닝 가능한 방법을 제공합니다. 단순히 스트림을 주어진 쌍의 writable 측에 연결하고, readable 측을 반환합니다.스트림을 파이프하면 파이프 작업이 끝날 때까지 잠금 상태가 되어, 다른 소비자가 리더를 획득할 수 없습니다.
await stream.
pipeTo
(destination[, {preventClose
,preventAbort
,preventCancel
,signal
}])-
이 읽기 가능한 스트림을 주어진 쓰기 가능한 스트림 destination에 파이프합니다. 다양한 오류 조건에서 파이프 동작을 세부 옵션으로 조정할 수 있습니다. 파이프가 정상적으로 완료되면 fulfill, 오류가 발생하면 reject되는 프로미스를 반환합니다.
스트림을 파이프하면 파이프 작업이 끝날 때까지 잠금 상태가 되어, 다른 소비자가 리더를 획득할 수 없습니다.
소스와 목적지 스트림의 오류 및 종료는 다음과 같이 전파됩니다:
-
이 소스 읽기 가능한 스트림에서 오류가 발생하면
preventAbort
옵션이 참이 아니면 destination이 중단(abort)됩니다. 반환된 프로미스는 소스의 오류 또는 목적지 중단 중 발생한 오류로 reject됩니다. -
destination에 오류가 발생하면
preventCancel
옵션이 참이 아니면 이 소스 읽기 가능한 스트림이 취소됩니다. 반환된 프로미스는 목적지의 오류 또는 소스 취소 중 발생한 오류로 reject됩니다. -
이 소스 읽기 가능한 스트림이 닫히면
preventClose
옵션이 참이 아니면 destination도 닫힙니다. 해당 과정이 끝나면 프로미스가 fulfill되며, 목적지 닫기 중 오류가 발생하면 그 오류로 reject됩니다. -
destination이 처음부터 닫혀 있거나 닫히는 중이면
preventCancel
옵션이 참이 아니면 이 소스 읽기 가능한 스트림이 취소됩니다. 반환된 프로미스는 닫힌 스트림으로 파이핑 실패 오류 또는 소스 취소 중 발생한 오류로 reject됩니다.
signal
옵션을AbortSignal
로 설정하면AbortController
를 통해 진행 중인 파이프 작업을 중단(abort)할 수 있습니다. 이 경우, 해당 소스 읽기 가능한 스트림은 취소되고 destination은 중단됩니다. 단, 각각preventCancel
또는preventAbort
옵션이 설정되어 있으면 해당 동작이 방지됩니다. -
[branch1, branch2] = stream.
tee
()-
이 읽기 가능한 스트림을 tee하여, 두 개의
ReadableStream
인스턴스로 이루어진 배열을 반환합니다.스트림을 tee하면 잠금 상태가 되어, 다른 소비자가 리더를 획득할 수 없습니다. 스트림을 취소하려면 두 분기 모두를 취소해야 하며, 복합 취소 사유가 스트림의 기초 소스에 전달됩니다.
이 스트림이 읽기 가능한 바이트 스트림이라면, 각 분기에 각 청크가 개별 복사본으로 전달됩니다. 그렇지 않으면 각 분기에서 동일한 객체를 참조하게 됩니다. 청크가 불변이 아니라면 두 분기 간 간섭이 발생할 수 있습니다.
new ReadableStream(underlyingSource, strategy)
생성자 단계는 다음과 같습니다:
-
underlyingSource가 없으면 null로 설정합니다.
-
underlyingSourceDict를 underlyingSource의 IDL 값으로 변환하여
UnderlyingSource
타입으로 만듭니다.underlyingSource 인자를 직접
UnderlyingSource
타입으로 선언할 수 없는 이유는 원래 객체의 참조가 사라지기 때문입니다. 원래 객체를 보존해야 다양한 메서드를 호출(invoke)할 수 있습니다. -
! InitializeReadableStream(this)를 수행합니다.
-
underlyingSourceDict["
type
"] 값이 "bytes
"라면:-
strategy["
size
"] 존재하면RangeError
예외를 throw합니다. -
highWaterMark를 ? ExtractHighWaterMark(strategy, 0)로 설정합니다.
-
? SetUpReadableByteStreamControllerFromUnderlyingSource(this, underlyingSource, underlyingSourceDict, highWaterMark)를 수행합니다.
-
-
그 외의 경우,
-
sizeAlgorithm을 ! ExtractSizeAlgorithm(strategy)로 설정합니다.
-
highWaterMark를 ? ExtractHighWaterMark(strategy, 1)로 설정합니다.
-
? SetUpReadableStreamDefaultControllerFromUnderlyingSource(this, underlyingSource, underlyingSourceDict, highWaterMark, sizeAlgorithm)을 수행합니다.
from(asyncIterable)
정적 메서드 단계는 다음과 같습니다:
-
? ReadableStreamFromIterable(asyncIterable)을 반환합니다.
locked
getter 단계는 다음과 같습니다:
-
! IsReadableStreamLocked(this)를 반환합니다.
cancel(reason)
메서드 단계는
다음과 같습니다:
-
! IsReadableStreamLocked(this)가 true이면
TypeError
예외로 거부된 프로미스(promise)를 반환합니다. -
! ReadableStreamCancel(this, reason)을 반환합니다.
getReader(options)
메서드 단계는 다음과 같습니다:
-
options["
mode
"] 값이 존재하지 않으면 ? AcquireReadableStreamDefaultReader(this)를 반환합니다. -
? AcquireReadableStreamBYOBReader(this)를 반환합니다.
function readAllChunks( readableStream) { const reader= readableStream. getReader(); const chunks= []; return pump(); function pump() { return reader. read(). then(({ value, done}) => { if ( done) { return chunks; } chunks. push( value); return pump(); }); } }
가장 먼저 리더를 획득한 후, 이후에는 리더만 독점적으로 사용합니다. 이렇게 하면 다른 소비자가 청크를 읽거나 스트림을 취소해서 방해할 수 없습니다.
pipeThrough(transform, options)
메서드 단계는 다음과 같습니다:
-
! IsReadableStreamLocked(this)가 true이면
TypeError
예외를 throw합니다. -
! IsWritableStreamLocked(transform["
writable
"]) 가 true이면TypeError
예외를 throw합니다. -
signal을 options["
signal
"] 값이 존재하면, 그 값으로, 아니면 undefined로 설정합니다. -
promise를 ! ReadableStreamPipeTo(this, transform["
writable
"], options["preventClose
"], options["preventAbort
"], options["preventCancel
"], signal)로 설정합니다. -
promise.[[PromiseIsHandled]]를 true로 설정합니다.
-
transform["
readable
"]를 반환합니다.
pipeThrough(transform, options)
를 활용한 파이프 체인 구성 예시는 다음과 같습니다:
httpResponseBody. pipeThrough( decompressorTransform) . pipeThrough( ignoreNonImageFilesTransform) . pipeTo( mediaGallery);
pipeTo(destination, options)
메서드 단계는 다음과 같습니다:
-
! IsReadableStreamLocked(this)가 true이면
TypeError
예외로 거부된 프로미스(promise)를 반환합니다. -
! IsWritableStreamLocked(destination) 가 true이면
TypeError
예외로 거부된 프로미스를 반환합니다. -
signal을 options["
signal
"] 값이 존재하면, 그 값으로, 아니면 undefined로 설정합니다. -
! ReadableStreamPipeTo(this, destination, options["
preventClose
"], options["preventAbort
"], options["preventCancel
"], signal)를 반환합니다.
AbortSignal
을
사용해 중단할 수 있습니다:
const controller= new AbortController(); readable. pipeTo( writable, { signal: controller. signal}); // ... 어느 시점에 ... controller. abort();
(위 코드는 pipeTo()
가
반환하는 프로미스에 대한 오류 처리를 생략했습니다.
또한 preventAbort
와
preventCancel
옵션으로 파이핑 중단 시 동작에 영향이 있으니 참고하세요.)
WritableStream
에
쓰면서 파이핑되는 ReadableStream
을
교체할 수 있습니다:
const controller= new AbortController(); const pipePromise= readable1. pipeTo( writable, { preventAbort: true , signal: controller. signal}); // ... 어느 시점에 ... controller. abort(); // 파이프가 완료될 때까지 기다린 뒤 새 파이프 시작: try { await pipePromise; } catch ( e) { // "AbortError" DOMException은 정상적으로 처리하고, 예기치 않은 오류는 재throw if ( e. name!== "AbortError" ) { throw e; } } // 새 파이프 시작! readable2. pipeTo( writable);
tee()
메서드 단계는 다음과 같습니다:
-
? ReadableStreamTee(this, false)를 반환합니다.
cacheEntry
와 원격 서버 업로드를 나타내는 writable 스트림 httpRequestBody
가 있다면, 동일한 읽기 가능한 스트림을
두 목적지에 동시에 파이프할 수 있습니다:
const [ forLocal, forRemote] = readableStream. tee(); Promise. all([ forLocal. pipeTo( cacheEntry), forRemote. pipeTo( httpRequestBody) ]) . then(() => console. log( "스트림을 캐시에 저장하고 업로드도 완료!" )) . catch ( e=> console. error( "캐싱 또는 업로드 실패: " , e));
4.2.5. 비동기 반복
for await (const chunk of stream) { ... }
for await (const chunk of stream.values({
preventCancel
: true })) { ... }-
스트림의 내부 큐에 있는 청크를 비동기적으로 반복(iterate)합니다.
스트림을 비동기적으로 반복하면 스트림을 잠금 상태로 만들어, 다른 소비자가 리더를 획득하지 못하게 합니다. async iterator의
return()
메서드가 호출되면(예:break
로 루프를 나갈 때) 잠금이 해제됩니다.기본적으로 async iterator의
return()
메서드를 호출하면 스트림도 취소됩니다. 이를 방지하려면values()
메서드를 사용하고preventCancel
옵션에 true를 전달하세요.
ReadableStream
기준, stream, iterator, args):
-
reader를 ? AcquireReadableStreamDefaultReader(stream)로 설정합니다.
-
iterator의 reader를 reader로 설정합니다.
-
preventCancel을 args[0]["
preventCancel
"]로 설정합니다. -
iterator의 취소 방지를 preventCancel로 설정합니다.
ReadableStream
기준, stream, iterator):
-
reader를 iterator의 reader로 설정합니다.
-
Assert: reader.[[stream]]이 undefined가 아님을 단언합니다.
-
promise를 새 프로미스로 설정합니다.
-
readRequest를 다음 읽기 요청으로 설정하며, 다음 항목을 포함합니다:
- 청크 단계, chunk 기준
-
-
Resolve promise을 chunk로 해결합니다.
-
- 닫기 단계
-
-
! ReadableStreamDefaultReaderRelease(reader)를 수행합니다.
-
Resolve promise을 end of iteration으로 해결합니다.
-
- 오류 단계, e 기준
-
-
! ReadableStreamDefaultReaderRelease(reader)를 수행합니다.
-
Reject promise을 e로 거부합니다.
-
-
! ReadableStreamDefaultReaderRead(this, readRequest)를 수행합니다.
-
promise를 반환합니다.
ReadableStream
기준, stream, iterator, arg):
-
reader를 iterator의 reader로 설정합니다.
-
Assert: reader.[[stream]]이 undefined가 아님을 단언합니다.
-
Assert: reader.[[readRequests]]가 비어 있음(empty)을 단언합니다. async iterator는 이전
next()
호출이 정산된 후에만 호출되도록 보장합니다. -
iterator의 취소 방지가 false라면:
-
result를 ! ReadableStreamReaderGenericCancel(reader, arg)로 설정합니다.
-
! ReadableStreamDefaultReaderRelease(reader)를 수행합니다.
-
result를 반환합니다.
-
-
! ReadableStreamDefaultReaderRelease(reader)를 수행합니다.
-
프로미스(resolve)된 undefined를 반환합니다.
4.2.6. postMessage()
를 통한 전송
destination.postMessage(rs, { transfer: [rs] });
-
ReadableStream
을 다른 프레임, 창, 또는 워커로 전송합니다.전송된 스트림은 원래 스트림과 동일하게 사용할 수 있습니다. 원래 스트림은 잠금 상태가 되어 직접 사용할 수 없게 됩니다.
ReadableStream
객체는 전송 가능한 객체입니다. 전송 단계는 value와 dataHolder가 주어졌을 때 다음과 같습니다:
-
! IsReadableStreamLocked(value)가 true이면 "
DataCloneError
"DOMException
을 throw합니다. -
port1을 새로운
MessagePort
로 현재 Realm에서 생성합니다. -
port2를 새로운
MessagePort
로 현재 Realm에서 생성합니다. -
Entangle port1과 port2를 연결(entangle)합니다.
-
writable을 새로운
WritableStream
으로 현재 Realm에서 생성합니다. -
! SetUpCrossRealmTransformWritable(writable, port1)를 수행합니다.
-
promise를 ! ReadableStreamPipeTo(value, writable, false, false, false)로 설정합니다.
-
promise.[[PromiseIsHandled]]를 true로 설정합니다.
-
dataHolder.[[port]]를 ! StructuredSerializeWithTransfer(port2, « port2 »)로 설정합니다.
-
deserializedRecord를 ! StructuredDeserializeWithTransfer(dataHolder.[[port]], 현재 Realm)로 설정합니다.
-
port를 deserializedRecord.[[Deserialized]]로 설정합니다.
-
! SetUpCrossRealmTransformReadable(value, port)를 수행합니다.
4.3. ReadableStreamGenericReader
믹스인
ReadableStreamGenericReader
믹스인은 ReadableStreamDefaultReader
와
ReadableStreamBYOBReader
객체들이 공유하는 공통 내부 슬롯, getter, 메서드를 정의합니다.
4.3.1. 믹스인 정의
ReadableStreamGenericReader
믹스인의 Web IDL 정의는 다음과 같습니다:
interface mixin {
ReadableStreamGenericReader readonly attribute Promise <undefined >closed ;Promise <undefined >cancel (optional any ); };
reason
4.3.2. 내부 슬롯
ReadableStreamGenericReader
믹스인을 포함하는 클래스 인스턴스는 아래 표에 설명된 내부 슬롯과 함께 생성됩니다:
내부 슬롯 | 설명 (비규범적) |
---|---|
[[closedPromise]] | 리더의 closed
getter가 반환한 프로미스
|
[[stream]] | 이 리더를 소유하는 ReadableStream
인스턴스
|
4.3.3. 메서드와 속성
closed
getter 단계는 다음과 같습니다:
-
this.[[closedPromise]]를 반환합니다.
cancel(reason)
메서드 단계는 다음과 같습니다:
-
this.[[stream]]이 undefined이면
TypeError
예외로 거부된 프로미스(promise)를 반환합니다. -
! ReadableStreamReaderGenericCancel(this, reason)을 반환합니다.
4.4. ReadableStreamDefaultReader
클래스
ReadableStreamDefaultReader
클래스는 기본 리더를 나타내며,
ReadableStream
인스턴스에서 제공(벤딩)하도록 설계되었습니다.
4.4.1. 인터페이스 정의
ReadableStreamDefaultReader
클래스의 Web IDL 정의는 다음과 같습니다:
[Exposed=*]interface {
ReadableStreamDefaultReader constructor (ReadableStream );
stream Promise <ReadableStreamReadResult >read ();undefined releaseLock (); };ReadableStreamDefaultReader includes ReadableStreamGenericReader ;dictionary {
ReadableStreamReadResult any ;
value boolean ; };
done
4.4.2. 내부 슬롯
ReadableStreamDefaultReader
인스턴스는 ReadableStreamGenericReader
에서
정의된 내부 슬롯과, 아래 표에 설명된 내부 슬롯을 가지고 생성됩니다:
내부 슬롯 | 설명 (비규범적) |
---|---|
[[readRequests]] | 리스트 타입의 읽기 요청(read request)들로, 소비자가 청크를 실제로 사용할 수 있을 때보다 더 일찍 요청할 때 사용됩니다 |
읽기 요청(read request)은 구조체(struct)로, 읽기 가능한 스트림의 내부 큐가 채워지거나 상태가 변경될 때 수행할 세 가지 알고리즘을 포함합니다. 다음의 항목을 가집니다:
- 청크 단계(chunk steps)
-
청크가 읽을 수 있게 되었을 때 호출되는, 청크를 인자로 받는 알고리즘
- 닫기 단계(close steps)
-
스트림이 닫혀서 더 이상 청크를 사용할 수 없을 때 호출되는, 인자가 없는 알고리즘
- 오류 단계(error steps)
-
스트림이 오류 상태가 되어 더 이상 청크를 사용할 수 없을 때 호출되는, JavaScript 값을 인자로 받는 알고리즘
4.4.3. 생성자, 메서드, 그리고 프로퍼티
reader = new
ReadableStreamDefaultReader
(stream)-
이는
stream.
를 호출하는 것과 동일합니다.getReader()
await reader.
closed
-
스트림이 닫힐 때 fulfilled되는 promise를 반환하며, 스트림에 오류가 발생하거나 reader의 lock이 스트림이 닫히기 전에 해제되면 rejected됩니다.
await reader.
cancel
([ reason ]){ value, done } = await reader.
read
()-
스트림의 내부 큐에서 다음 chunk에 접근할 수 있는 promise를 반환합니다(사용 가능한 경우).
- chunk가 사용 가능해지면, promise는 다음 형태의 객체로 fulfilled됩니다:
.{ value: theChunk, done: false } - 스트림이 닫히면, promise는 다음 형태의 객체로 fulfilled됩니다:
.{ value: undefined , done: true } - 스트림에 오류가 발생하면, promise는 해당 오류로 rejected됩니다.
chunk를 읽어서 큐가 비어 있으면, 더 많은 데이터가 underlying source에서 pull됩니다.
- chunk가 사용 가능해지면, promise는 다음 형태의 객체로 fulfilled됩니다:
reader.
releaseLock
()-
스트림에 대한 reader의 lock을 해제합니다. lock이 해제된 후에는 reader가 더 이상 active 상태가 아닙니다. lock 해제 시 연관된 스트림이 오류 상태라면 reader도 이후 동일하게 오류 상태로 나타나며, 그렇지 않으면 reader는 닫힌 상태로 나타납니다.
reader의 lock이 해제될 때 pending read 요청이 남아있다면, reader의
read()
메서드가 반환한 promise들은 즉시TypeError
로 rejected됩니다. 읽히지 않은 chunk들은 스트림의 internal queue에 남아있으며, 새 reader를 획득하여 나중에 읽을 수 있습니다.
new ReadableStreamDefaultReader(stream)
생성자 단계는 다음과 같습니다:
-
? SetUpReadableStreamDefaultReader(this, stream)을 수행합니다.
read()
메서드 단계는 다음과 같습니다:
-
this.[[stream]]이 undefined이면, a promise rejected with
TypeError
exception을 반환합니다. -
promise를 a new promise로 둡니다.
-
readRequest를 다음 read request와 items로 둡니다:
- chunk steps, chunk이 주어진 경우
- close steps
- error steps, e가 주어진 경우
-
-
Reject promise를 e로 거부합니다.
-
-
! ReadableStreamDefaultReaderRead(this, readRequest)를 수행합니다.
-
promise를 반환합니다.
releaseLock()
메서드 단계는 다음과 같습니다:
-
this.[[stream]]이 undefined이면 반환합니다.
-
! ReadableStreamDefaultReaderRelease(this)를 수행합니다.
4.5. ReadableStreamBYOBReader
클래스
ReadableStreamBYOBReader
클래스는 BYOB 리더를 나타내며,
ReadableStream
인스턴스에서 제공되도록 설계되었습니다.
4.5.1. 인터페이스 정의
ReadableStreamBYOBReader
클래스의 Web IDL 정의는 다음과 같습니다:
[Exposed=*]interface {
ReadableStreamBYOBReader constructor (ReadableStream );
stream Promise <ReadableStreamReadResult >read (ArrayBufferView ,
view optional ReadableStreamBYOBReaderReadOptions = {});
options undefined releaseLock (); };ReadableStreamBYOBReader includes ReadableStreamGenericReader ;dictionary { [
ReadableStreamBYOBReaderReadOptions EnforceRange ]unsigned long long = 1; };
min
4.5.2. 내부 슬롯
ReadableStreamBYOBReader
인스턴스는
ReadableStreamGenericReader
에
정의된 내부 슬롯과 아래 표에 설명된 슬롯으로 생성됩니다.
내부 슬롯 | 설명 (비규범적) |
---|---|
[[readIntoRequests]] | 리스트 형태의 read-into 요청 집합으로, consumer가 chunk를 이용 가능 시점보다 먼저 요청하는 경우에 사용됩니다. |
read-into 요청은 struct로, readable byte stream의 internal queue가 채워지거나 상태가 변경될 때 수행하는 세 가지 알고리즘을 포함합니다. 다음 항목을 가집니다:
- chunk steps
-
읽을 수 있는 chunk가 있을 때 호출되는 알고리즘
- close steps
-
스트림이 닫혀서 chunk 또는 undefined일 때 호출되는 알고리즘
- error steps
-
스트림이 오류 상태여서 chunk를 얻을 수 없을 때, JavaScript 값을 인자로 호출되는 알고리즘
close steps는 chunk를 인자로 받아, 가능하다면 backing memory를 호출자에게 반환할 수 있도록 합니다. 예를 들어,
byobReader.read(chunk)
호출 시 닫힌 스트림에서는
로 fulfill됩니다. 만약
스트림이
취소된 경우,
backing memory는 폐기되고
byobReader.read(chunk)
호출은 전통적인
형태로 fulfill됩니다.
4.5.3. 생성자, 메서드, 그리고 프로퍼티
reader = new
ReadableStreamBYOBReader
(stream)await reader.
closed
-
스트림이 닫힐 때 fulfilled되는 promise를 반환하며, 스트림에 오류가 발생하거나 reader의 lock이 스트림이 닫히기 전에 해제되면 rejected됩니다.
await reader.
cancel
([ reason ]){ value, done } = await reader.
read
(view[, {min
}])-
view에 바이트를 읽으려고 시도하며, 결과로 resolve되는 promise를 반환합니다:
- chunk가 사용 가능해지면, promise는 다음 형태의 객체로 fulfilled됩니다:
. 이 경우 view는 detached되어 더 이상 사용할 수 없으며,{ value: newView, done: false } newView
는 동일한 backing memory 영역에 대한 새로운 view(동일 유형)로 chunk 데이터가 기록되어 있습니다. - 스트림이 닫히면, promise는 다음 형태의 객체로 fulfilled됩니다:
. 이 경우 view는 detached되어 더 이상 사용할 수 없으며,{ value: newView, done: true } newView
는 동일한 backing memory 영역에 대한 새로운 view(동일 유형)로, 변경 없이 호출자에게 메모리가 반환됨을 보장합니다. - reader가 취소된 경우, promise는 다음 형태의 객체로 fulfilled됩니다:
. 이 경우 view의 backing memory 영역은 폐기되어 호출자에게 반환되지 않습니다.{ value: undefined , done: true } - 스트림에 오류가 발생하면, promise는 해당 오류로 rejected됩니다.
chunk를 읽어서 큐가 비어 있으면, 더 많은 데이터가 underlying source에서 pull됩니다.
min
값이 주어진 경우, promise는 주어진 최소 개수의 요소가 사용 가능해질 때에만 fulfilled됩니다. 여기서 "요소 개수"는newView
의length
(typed array의 경우) 또는newView
의byteLength
(DataView
의 경우)로 계산합니다. 스트림이 닫히면, promise는 스트림에 남아 있는 요소(요청한 양보다 적을 수 있음)로 fulfilled됩니다. 값이 주어지지 않으면, promise는 최소 한 요소가 사용 가능해질 때 resolve됩니다. - chunk가 사용 가능해지면, promise는 다음 형태의 객체로 fulfilled됩니다:
reader.
releaseLock
()-
스트림에 대한 reader의 lock을 해제합니다. lock이 해제된 후에는 reader가 더 이상 active 상태가 아닙니다. lock 해제 시 연관된 스트림이 오류 상태라면 reader도 이후 동일하게 오류 상태로 나타나며, 그렇지 않으면 reader는 닫힌 상태로 나타납니다.
reader의 lock이 해제될 때 pending read 요청이 남아있다면, reader의
read()
메서드가 반환한 promise들은 즉시TypeError
로 rejected됩니다. 읽히지 않은 chunk들은 스트림의 internal queue에 남아있으며, 새 reader를 획득하여 나중에 읽을 수 있습니다.
new ReadableStreamBYOBReader(stream)
생성자 단계는 다음과 같습니다:
-
? SetUpReadableStreamBYOBReader(this, stream)을 수행합니다.
read(view, options)
메서드 단계는 다음과 같습니다:
-
view.[[ByteLength]]가 0이면, a promise rejected with
TypeError
예외를 반환합니다. -
view.[[ViewedArrayBuffer]].[[ByteLength]]가 0이면, a promise rejected with
TypeError
예외를 반환합니다. -
! IsDetachedBuffer(view.[[ViewedArrayBuffer]])가 true이면, a promise rejected with
TypeError
예외를 반환합니다. -
options["
min
"] 값이 0이면, a promise rejected withTypeError
예외를 반환합니다. -
view에 [[TypedArrayName]] 내부 슬롯이 있다면,
-
options["
min
"] 값이 view.[[ArrayLength]]보다 크면, a promise rejected withRangeError
예외를 반환합니다.
-
-
그 외(
DataView
인 경우),-
options["
min
"] 값이 view.[[ByteLength]]보다 크면, a promise rejected withRangeError
예외를 반환합니다.
-
-
this.[[stream]]이 undefined이면, a promise rejected with
TypeError
예외를 반환합니다. -
promise를 a new promise로 둡니다.
-
readIntoRequest를 다음 read-into 요청과 항목으로 둡니다:
- chunk steps, chunk가 주어진 경우
- close steps, chunk가 주어진 경우
- error steps, e가 주어진 경우
-
-
Reject promise를 e로 거부합니다.
-
-
! ReadableStreamBYOBReaderRead(this, view, options["
min
"], readIntoRequest)를 수행합니다. -
promise를 반환합니다.
releaseLock()
메서드 단계는 다음과 같습니다:
-
this.[[stream]]이 undefined이면 반환합니다.
-
! ReadableStreamBYOBReaderRelease(this)를 수행합니다.
4.6.
ReadableStreamDefaultController
클래스
ReadableStreamDefaultController
클래스는 ReadableStream
의
상태 및 내부 큐를 제어할 수 있는 메서드를
제공합니다.
readable byte
stream이 아닌
ReadableStream
을
생성할 때,
underlying source에 해당
ReadableStreamDefaultController
인스턴스가 제공되어 조작할 수 있습니다.
4.6.1. 인터페이스 정의
ReadableStreamDefaultController
클래스의 Web IDL 정의는 다음과 같습니다:
[Exposed=*]interface {
ReadableStreamDefaultController readonly attribute unrestricted double ?desiredSize ;undefined close ();undefined enqueue (optional any );
chunk undefined error (optional any ); };
e
4.6.2. 내부 슬롯
ReadableStreamDefaultController
인스턴스는 아래 표에 설명된 내부 슬롯과 함께 생성됩니다:
내부 슬롯 | 설명 (비규범적) |
---|---|
[[cancelAlgorithm]] | 하나의 인자(cancel 이유)를 받는 promise를 반환하는 알고리즘으로, underlying source에 취소 요청을 전달합니다 |
[[closeRequested]] | 불리언 플래그로, 스트림이 그 underlying source에 의해 닫혔으나, 아직 읽히지 않은 chunk들이 내부 큐에 남아 있는지를 나타냅니다 |
[[pullAgain]] | 스트림 메커니즘이 더 많은 데이터를 pull하기 위해 underlying source의 pull 알고리즘을 호출하도록 요청했으나, 이전 호출이 아직 실행 중일 경우 true로 설정되는 불리언 플래그 |
[[pullAlgorithm]] | underlying source에서 데이터를 pull하는 promise를 반환하는 알고리즘 |
[[pulling]] | underlying source의 pull 알고리즘이 실행 중이며 반환된 promise가 아직 fulfilled되지 않았을 때 true로 설정되는 불리언 플래그로, 재진입 호출을 방지하기 위해 사용됨 |
[[queue]] | 리스트로, 스트림의 chunk들이 저장된 내부 큐를 나타냅니다 |
[[queueTotalSize]] | [[queue]]에 저장된 모든 chunk의 총 크기 (자세한 내용은 § 8.1 Queue-with-sizes 참고) |
[[started]] | underlying source가 시작을 마쳤는지 여부를 나타내는 불리언 플래그 |
[[strategyHWM]] | 스트림의 큐잉 전략의 일부로 생성자에 제공되는 숫자 값으로, 스트림이 역압을 underlying source에 적용하는 시점을 나타냅니다 |
[[strategySizeAlgorithm]] | chunk가 enqueue될 때 크기를 계산하기 위한 알고리즘으로, 스트림의 큐잉 전략의 일부입니다 |
[[stream]] | 제어되는 ReadableStream
인스턴스
|
4.6.3. 메서드 및 프로퍼티
desiredSize = controller.
desiredSize
-
제어중인 스트림의 내부 큐를 채우기 위한 원하는 크기를 반환합니다. 이 값은 큐가 초과로 채워져 있을 경우 음수일 수 있습니다. underlying source에서는 이 정보를 활용해 언제, 어떻게 역압을 적용할지 결정할 수 있습니다.
controller.
close
()-
제어중인 readable stream을 닫습니다. 소비자는 스트림에 이미 enqueue된 chunk들을 계속 읽을 수 있지만, 모두 읽은 후에는 스트림이 닫히게 됩니다.
controller.
enqueue
(chunk)-
제어중인 readable stream에 chunk chunk를 enqueue 합니다.
controller.
error
(e)-
제어중인 readable stream에 오류를 발생시켜, 이후 모든 동작이 해당 오류 e로 실패하게 만듭니다.
desiredSize
getter 단계는 다음과 같습니다:
close()
메서드 단계는 다음과 같습니다:
-
! ReadableStreamDefaultControllerCanCloseOrEnqueue(this)가 false이면,
TypeError
예외를 throw 합니다. -
! ReadableStreamDefaultControllerClose(this)를 수행합니다.
enqueue(chunk)
메서드 단계는 다음과 같습니다:
-
! ReadableStreamDefaultControllerCanCloseOrEnqueue(this)가 false이면,
TypeError
예외를 throw 합니다. -
? ReadableStreamDefaultControllerEnqueue(this, chunk)를 수행합니다.
error(e)
메서드 단계는 다음과 같습니다:
-
! ReadableStreamDefaultControllerError(this, e)를 수행합니다.
4.6.4. 내부 메서드
다음은 각 ReadableStreamDefaultController
인스턴스가 구현하는 내부 메서드입니다.
readable stream 구현체는 § 4.9.2 컨트롤러와의 인터페이스에서 논의된 대로 BYOB
컨트롤러의 대응 메서드 또는 이 메서드들을 다형적으로 호출합니다.
-
! ResetQueue(this)를 수행합니다.
-
result를 this.[[cancelAlgorithm]]를 reason을 전달하여 실행한 결과로 둡니다.
-
! ReadableStreamDefaultControllerClearAlgorithms(this)를 수행합니다.
-
result를 반환합니다.
-
stream을 this.[[stream]]으로 둡니다.
-
-
chunk를 ! DequeueValue(this)로 둡니다.
-
this.[[closeRequested]] 가 true이고 this.[[queue]]가 비어 있다면,
-
! ReadableStreamDefaultControllerClearAlgorithms(this)를 수행합니다.
-
! ReadableStreamClose(stream)을 수행합니다.
-
-
그 외에는, ! ReadableStreamDefaultControllerCallPullIfNeeded(this)를 수행합니다.
-
readRequest의 chunk steps를 chunk를 인자로 하여 수행합니다.
-
-
그 외에는,
-
! ReadableStreamAddReadRequest(stream, readRequest)를 수행합니다.
-
! ReadableStreamDefaultControllerCallPullIfNeeded(this)를 수행합니다.
-
-
반환합니다.
4.7. ReadableByteStreamController
클래스
ReadableByteStreamController
클래스는 ReadableStream
의
상태와 내부 큐를 제어할 수 있는 메서드를
제공합니다.
바이트 스트림을 읽을 수 있는
ReadableStream
을
생성할 때,
underlying source에 해당
ReadableByteStreamController
인스턴스가 제공되어 조작할 수 있습니다.
4.7.1. 인터페이스 정의
ReadableByteStreamController
클래스의 Web IDL 정의는 다음과 같습니다:
[Exposed=*]interface {
ReadableByteStreamController readonly attribute ReadableStreamBYOBRequest ?byobRequest ;readonly attribute unrestricted double ?desiredSize ;undefined close ();undefined enqueue (ArrayBufferView );
chunk undefined error (optional any ); };
e
4.7.2. 내부 슬롯
ReadableByteStreamController
인스턴스는 아래 표에 설명된 내부 슬롯과 함께 생성됩니다:
내부 슬롯 | 설명 (비규범적) |
---|---|
[[autoAllocateChunkSize]] | 자동 버퍼 할당 기능이 활성화된 경우, 양수 정수로 이 값이 버퍼 할당 크기를 지정합니다. 그렇지 않으면 undefined입니다. |
[[byobRequest]] | 현재 BYOB pull 요청을 나타내는 ReadableStreamBYOBRequest
인스턴스로, 대기 중인 요청이 없으면 null입니다.
|
[[cancelAlgorithm]] | 하나의 인자(취소 이유)를 받는 promise를 반환하는 알고리즘으로, underlying byte source에 취소 요청을 전달합니다. |
[[closeRequested]] | 불리언 플래그로, 스트림이 underlying byte source에 의해 닫혔으나, 아직 읽히지 않은 chunk들이 내부 큐에 남아 있는지를 나타냅니다. |
[[pullAgain]] | 스트림 메커니즘이 더 많은 데이터를 pull하기 위해 underlying byte source의 pull 알고리즘을 호출하도록 요청했으나, 이전 호출이 아직 실행 중일 경우 true로 설정되는 불리언 플래그 |
[[pullAlgorithm]] | underlying byte source에서 데이터를 pull하는 promise를 반환하는 알고리즘 |
[[pulling]] | underlying byte source의 pull 알고리즘이 실행 중이며 반환된 promise가 아직 fulfilled되지 않았을 때 true로 설정되는 불리언 플래그로, 재진입 호출을 방지하기 위해 사용됨 |
[[pendingPullIntos]] | 리스트 형태의 pull-into descriptor 집합입니다. |
[[queue]] | 리스트 형태의 readable byte stream queue entry로, 스트림의 내부 큐에 저장된 chunk들을 나타냅니다. |
[[queueTotalSize]] | [[queue]]에 저장된 모든 chunk의 총 바이트 크기 (자세한 내용은 § 8.1 Queue-with-sizes 참고) |
[[started]] | underlying byte source가 시작을 마쳤는지 여부를 나타내는 불리언 플래그 |
[[strategyHWM]] | 스트림의 큐잉 전략의 일부로 생성자에 제공되는 숫자 값으로, 스트림이 역압을 underlying byte source에 적용하는 시점을 나타냅니다. |
[[stream]] | 제어되는 ReadableStream
인스턴스
|
ReadableByteStreamController
인스턴스는 [[queue]]와 [[queueTotalSize]]
슬롯을 가지지만, 큐를 다루는 방식이 명세의 다른 부분과 다르기 때문에, § 8.1 Queue-with-sizes의 대부분 추상
연산을 사용하지 않습니다. 대신 이 두 슬롯을 직접 수동으로 갱신합니다.
이는 향후 명세 리팩토링에서 정리될 수 있습니다.
readable byte stream queue entry는 struct로, chunk의 중요한 측면을 readable byte stream의 특정 사례에 대해 캡슐화합니다. 다음 항목을 가집니다:
- buffer
-
ArrayBuffer
로, 전송된 형태로 underlying byte source가 처음 제공한 것을 나타냅니다. - byte offset
-
원래 underlying byte source가 제공한 view에서 파생된 바이트 오프셋을 나타내는 0 이상의 정수입니다.
- byte length
-
원래 underlying byte source가 제공한 view에서 파생된 바이트 길이를 나타내는 0 이상의 정수입니다.
pull-into descriptor는 struct로, 대기 중인 BYOB pull 요청을 나타냅니다. 다음 항목을 가집니다:
- buffer
- buffer byte length
-
buffer의 초기 바이트 길이를 나타내는 양수 정수입니다.
- byte offset
-
buffer에서 underlying byte source가 쓰기를 시작할 바이트 오프셋을 나타내는 0 이상의 정수입니다.
- byte length
-
buffer에 쓸 수 있는 바이트 수를 나타내는 양수 정수입니다.
- bytes filled
-
현재까지 buffer에 쓰인 바이트 수를 나타내는 0 이상의 정수입니다.
- minimum fill
-
buffer에 반드시 쓰여야 하는 최소 바이트 수를 나타내는 양수 정수로, 관련
read()
요청이 fulfilled될 수 있는 조건입니다. 기본값은 element size와 같습니다. - element size
-
buffer에 한 번에 쓸 수 있는 바이트 수를 나타내는 양수 정수로, view constructor로 설명된 타입의 view를 사용합니다.
- view constructor
-
typed array constructor 또는
%DataView%
로, buffer에 쓸 view를 생성하는 데 사용됩니다. - reader type
-
"
default
" 또는 "byob
"로, 어떤 타입의 readable stream reader가 이 요청을 시작했는지를 나타내며, reader가 해제된 경우에는 "none
"입니다.
4.7.3. 메서드 및 프로퍼티
byobRequest = controller.
byobRequest
-
현재 BYOB pull 요청을 반환하며, 없으면 null을 반환합니다.
desiredSize = controller.
desiredSize
-
제어되는 스트림의 내부 큐를 채우기 위한 원하는 크기를 반환합니다. 큐가 과도하게 채워져 있을 경우 음수가 될 수 있습니다. underlying byte source는 이 정보를 활용하여 언제, 어떻게 역압을 적용할지 결정할 수 있습니다.
controller.
close
()-
제어되는 readable stream을 닫습니다. 소비자는 스트림에 이미 enqueue된 chunk들을 계속 읽을 수 있지만, 모두 읽은 후에는 스트림이 닫히게 됩니다.
controller.
enqueue
(chunk)-
제어되는 readable stream에 chunk chunk를 enqueue합니다. chunk는
ArrayBufferView
인스턴스여야 하며, 그렇지 않으면TypeError
가 발생합니다. controller.
error
(e)-
제어되는 readable stream에 오류를 발생시켜, 이후 모든 동작이 해당 오류 e로 실패하게 만듭니다.
byobRequest
getter 단계는 다음과 같습니다:
-
! ReadableByteStreamControllerGetBYOBRequest(this)를 반환합니다.
desiredSize
getter 단계는 다음과 같습니다:
-
! ReadableByteStreamControllerGetDesiredSize(this)를 반환합니다.
close()
메서드 단계는 다음과 같습니다:
-
this.[[closeRequested]]가 true이면,
TypeError
예외를 throw합니다. -
this.[[stream]].[[state]]가 "
readable
"가 아니면TypeError
예외를 throw합니다. -
? ReadableByteStreamControllerClose(this)를 수행합니다.
enqueue(chunk)
메서드 단계는 다음과 같습니다:
-
chunk.[[ByteLength]]가 0이면
TypeError
예외를 throw합니다. -
chunk.[[ViewedArrayBuffer]].[[ByteLength]]가 0이면
TypeError
예외를 throw합니다. -
this.[[closeRequested]]가 true이면
TypeError
예외를 throw합니다. -
this.[[stream]].[[state]]가 "
readable
"가 아니면TypeError
예외를 throw합니다. -
? ReadableByteStreamControllerEnqueue(this, chunk)를 반환합니다.
error(e)
메서드 단계는 다음과 같습니다:
-
! ReadableByteStreamControllerError(this, e)를 수행합니다.
4.7.4. 내부 메서드
다음은 각 ReadableByteStreamController
인스턴스가 구현하는 내부 메서드입니다.
readable stream 구현체는 § 4.9.2 컨트롤러와의 인터페이스에서 논의된 대로, 기본
컨트롤러의 대응 메서드 또는 이 메서드들을 다형적으로 호출합니다.
-
! ReadableByteStreamControllerClearPendingPullIntos(this)를 수행합니다.
-
! ResetQueue(this)를 수행합니다.
-
result를 this.[[cancelAlgorithm]]를 reason을 전달하여 실행한 결과로 둡니다.
-
! ReadableByteStreamControllerClearAlgorithms(this)를 수행합니다.
-
result를 반환합니다.
-
stream을 this.[[stream]]으로 둡니다.
-
Assert: ! ReadableStreamHasDefaultReader(stream) 가 true임을 보장합니다.
-
this.[[queueTotalSize]] > 0이라면,
-
Assert: ! ReadableStreamGetNumReadRequests(stream) 가 0임을 보장합니다.
-
! ReadableByteStreamControllerFillReadRequestFromQueue(this, readRequest)를 수행합니다.
-
반환합니다.
-
-
autoAllocateChunkSize를 this.[[autoAllocateChunkSize]]로 둡니다.
-
autoAllocateChunkSize가 undefined가 아니라면,
-
buffer를 Construct(
%ArrayBuffer%
, « autoAllocateChunkSize »)로 둡니다. -
buffer가 abrupt completion이라면,
-
readRequest의 error steps를 buffer.[[Value]]로 실행합니다.
-
반환합니다.
-
-
pullIntoDescriptor를 다음 속성으로 새 pull-into descriptor로 둡니다.
- buffer
- buffer.[[Value]]
- buffer byte length
- autoAllocateChunkSize
- byte offset
- 0
- byte length
- autoAllocateChunkSize
- bytes filled
- 0
- minimum fill
- 1
- element size
- 1
- view constructor
%Uint8Array%
- reader type
- "
default
"
-
Append pullIntoDescriptor를 this.[[pendingPullIntos]]에 추가합니다.
-
-
! ReadableStreamAddReadRequest(stream, readRequest)를 수행합니다.
-
! ReadableByteStreamControllerCallPullIfNeeded(this)를 수행합니다.
-
this.[[pendingPullIntos]]가 비어 있지 않다면,
-
firstPendingPullInto를 this.[[pendingPullIntos]][0]로 둡니다.
-
firstPendingPullInto의 reader type을 "
none
"으로 설정합니다. -
this.[[pendingPullIntos]]를 리스트 « firstPendingPullInto »로 설정합니다.
-
4.8. ReadableStreamBYOBRequest
클래스
ReadableStreamBYOBRequest
클래스는
ReadableByteStreamController
에서
pull-into 요청을 나타냅니다.
4.8.1. 인터페이스 정의
ReadableStreamBYOBRequest
클래스의 Web IDL 정의는 다음과 같습니다:
[Exposed=*]interface {
ReadableStreamBYOBRequest readonly attribute ArrayBufferView ?view ;undefined respond ([EnforceRange ]unsigned long long );
bytesWritten undefined respondWithNewView (ArrayBufferView ); };
view
4.8.2. 내부 슬롯
ReadableStreamBYOBRequest
인스턴스는 아래 표에 설명된 내부 슬롯과 함께 생성됩니다:
내부 슬롯 | 설명 (비규범적) |
---|---|
[[controller]] | 상위 ReadableByteStreamController
인스턴스
|
[[view]] | typed array로, 컨트롤러가 생성된 데이터를 쓸 수 있는 대상 영역을 나타내며, BYOB 요청이 무효화된 후에는 null이 됩니다. |
4.8.3. 메서드 및 프로퍼티
view = byobRequest.
view
-
쓰기 대상 view를 반환하며, BYOB 요청이 이미 응답된 경우 null을 반환합니다.
byobRequest.
respond
(bytesWritten)-
연관된 readable byte stream에 bytesWritten 바이트가
view
에 기록되었음을 알리며, 결과가 consumer에게 전달됩니다. byobRequest.
respondWithNewView
(view)-
연관된 readable byte stream에,
view
에 쓰는 대신 underlying byte source가 새로운ArrayBufferView
를 제공함을 나타내며, 이는 consumer에게 전달됩니다.새로운 view는 반드시
view
와 동일한 backing memory 영역의 view여야 합니다. 즉, buffer가 같거나 (전송된 버전이어야 하며),view
의 buffer와 같아야 합니다.byteOffset
도view
의byteOffset
과 같아야 하며,byteLength
(기록된 바이트 수)는view
의byteLength
이하여야 합니다.이 메서드가 호출된 후 view는 전송되며 더 이상 수정할 수 없습니다.
respond(bytesWritten)
메서드 단계는 다음과
같습니다:
-
this.[[controller]]가 undefined이면,
TypeError
예외를 throw합니다. -
! IsDetachedBuffer(this.[[view]].[[ArrayBuffer]]) 가 true이면
TypeError
예외를 throw합니다. -
Assert: this.[[view]].[[ViewedArrayBuffer]].[[ByteLength]] > 0.
-
? ReadableByteStreamControllerRespond(this.[[controller]], bytesWritten)를 수행합니다.
respondWithNewView(view)
메서드
단계는 다음과 같습니다:
-
this.[[controller]]가 undefined이면,
TypeError
예외를 throw합니다. -
! IsDetachedBuffer(view.[[ViewedArrayBuffer]]) 가 true이면,
TypeError
예외를 throw합니다. -
? ReadableByteStreamControllerRespondWithNewView(this.[[controller]], view)를 반환합니다.
4.9. 추상 연산
4.9.1. ReadableStream 다루기
다음 추상 연산들은 ReadableStream
인스턴스에 대해 더 높은 수준에서 동작합니다.
-
reader를 new
ReadableStreamBYOBReader
로 둡니다. -
? SetUpReadableStreamBYOBReader(reader, stream)를 수행합니다.
-
reader를 반환합니다.
-
reader를 new
ReadableStreamDefaultReader
로 둡니다. -
? SetUpReadableStreamDefaultReader(reader, stream)를 수행합니다.
-
reader를 반환합니다.
-
highWaterMark가 전달되지 않았다면, 1로 설정합니다.
-
sizeAlgorithm가 전달되지 않았다면, 1을 반환하는 알고리즘으로 설정합니다.
-
Assert: ! IsNonNegativeNumber(highWaterMark)가 true임을 보장합니다.
-
stream을 new
ReadableStream
로 둡니다. -
! InitializeReadableStream(stream)을 수행합니다.
-
controller를 new
ReadableStreamDefaultController
로 둡니다. -
? SetUpReadableStreamDefaultController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm)를 수행합니다.
-
stream을 반환합니다.
이 추상 연산은 전달된 startAlgorithm이 예외를 발생시킬 경우에만 예외를 throw합니다.
-
stream을 new
ReadableStream
로 둡니다. -
! InitializeReadableStream(stream)을 수행합니다.
-
controller를 new
ReadableByteStreamController
로 둡니다. -
? SetUpReadableByteStreamController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, 0, undefined)를 수행합니다.
-
stream을 반환합니다.
이 추상 연산은 전달된 startAlgorithm이 예외를 발생시킬 경우에만 예외를 throw합니다.
-
stream.[[state]]를 "
readable
"로 설정합니다. -
stream.[[reader]]와 stream.[[storedError]]를 undefined로 설정합니다.
-
stream.[[disturbed]]를 false로 설정합니다.
-
stream.[[reader]]가 undefined이면 false를 반환합니다.
-
true를 반환합니다.
-
stream을 undefined로 둡니다.
-
iteratorRecord를 ? GetIterator(asyncIterable, async)로 둡니다.
-
startAlgorithm을 undefined를 반환하는 알고리즘으로 둡니다.
-
pullAlgorithm을 다음 단계로 둡니다:
-
nextResult를 IteratorNext(iteratorRecord)로 둡니다.
-
nextResult가 abrupt completion이면 a promise rejected with nextResult.[[Value]]를 반환합니다.
-
nextPromise를 a promise resolved with nextResult.[[Value]]로 둡니다.
-
nextPromise에 대해 reacting을 아래 fulfillment 단계로 수행한 결과를 반환합니다. iterResult가 주어집니다:
-
iterResult가 Object가 아니면
TypeError
를 throw합니다. -
done을 ? IteratorComplete(iterResult)로 둡니다.
-
done이 true이면:
-
! ReadableStreamDefaultControllerClose(stream.[[controller]])를 수행합니다.
-
-
그 외에는:
-
value를 ? IteratorValue(iterResult)로 둡니다.
-
! ReadableStreamDefaultControllerEnqueue(stream.[[controller]], value)를 수행합니다.
-
-
-
-
cancelAlgorithm을 다음 단계로 둡니다. reason이 주어집니다:
-
iterator를 iteratorRecord.[[Iterator]]로 둡니다.
-
returnMethod를 GetMethod(iterator, "
return
")로 둡니다. -
returnMethod가 abrupt completion이면 a promise rejected with returnMethod.[[Value]]를 반환합니다.
-
returnMethod.[[Value]]가 undefined이면 a promise resolved with undefined를 반환합니다.
-
returnResult를 Call(returnMethod.[[Value]], iterator, « reason »)로 둡니다.
-
returnResult가 abrupt completion이면 a promise rejected with returnResult.[[Value]]를 반환합니다.
-
returnPromise를 a promise resolved with returnResult.[[Value]]로 둡니다.
-
returnPromise에 대해 reacting을 아래 fulfillment 단계로 수행한 결과를 반환합니다. iterResult가 주어집니다:
-
iterResult가 Object가 아니면
TypeError
를 throw합니다. -
undefined를 반환합니다.
-
-
-
stream을 ! CreateReadableStream(startAlgorithm, pullAlgorithm, cancelAlgorithm, 0)로 설정합니다.
-
stream을 반환합니다.
-
Assert: source가 구현하는
ReadableStream
임을 보장합니다. -
Assert: dest가 구현하는
WritableStream
임을 보장합니다. -
Assert: preventClose, preventAbort, preventCancel 모두 boolean임을 보장합니다.
-
signal이 주어지지 않았다면 signal을 undefined로 둡니다.
-
Assert: signal이 undefined이거나, signal이 구현하는
AbortSignal
임을 보장합니다. -
Assert: ! IsReadableStreamLocked(source)가 false임을 보장합니다.
-
Assert: ! IsWritableStreamLocked(dest)가 false임을 보장합니다.
-
source.[[controller]]가 구현하는
ReadableByteStreamController
인 경우, reader를 ! AcquireReadableStreamBYOBReader(source) 또는 ! AcquireReadableStreamDefaultReader(source)로 둡니다(사용자 에이전트의 재량). -
그 외에는 reader를 ! AcquireReadableStreamDefaultReader(source)로 둡니다.
-
writer를 ! AcquireWritableStreamDefaultWriter(dest)로 둡니다.
-
source.[[disturbed]]를 true로 설정합니다.
-
shuttingDown을 false로 둡니다.
-
promise를 a new promise로 둡니다.
-
signal이 undefined가 아니라면,
-
abortAlgorithm을 다음 단계로 둡니다:
-
error를 signal의 abort reason으로 둡니다.
-
actions를 빈 ordered set으로 둡니다.
-
preventAbort가 false이면 append 아래 액션을 actions에 추가합니다:
-
dest.[[state]]가 "
writable
"이면 ! WritableStreamAbort(dest, error)를 반환합니다. -
그 외에는 a promise resolved with undefined를 반환합니다.
-
-
preventCancel가 false이면 append 아래 액션을 actions에 추가합니다:
-
source.[[state]]가 "
readable
"이면 ! ReadableStreamCancel(source, error)를 반환합니다. -
그 외에는 a promise resolved with undefined를 반환합니다.
-
-
Shutdown with an action 으로 actions의 모든 액션을 기다리는 promise와 error를 전달합니다.
-
-
signal이 aborted이면 abortAlgorithm을 수행하고 promise를 반환합니다.
-
Add abortAlgorithm을 signal에 추가합니다.
-
-
병렬로 (실제로는 아님; #905 참고), reader와 writer를 사용하여 source에서 모든 chunk를 읽어 dest에 씁니다. reader와 writer의 locking 때문에, 실제 어떻게 동작하는지는 작성자 코드로 관찰할 수 없으므로 구현 방식에 융통성이 있습니다. 아래 제약사항은 알고리즘이 어떻게 구현되든 적용됩니다:
-
공개 API를 사용하지 말 것: 읽기/쓰기 또는 아래 연산을 수행하는 동안, JavaScript로 수정 가능한 reader, writer, stream API(적절한 prototype의 메서드)들은 사용하지 않고 stream을 직접 조작해야 합니다.
-
역압(backpressure)을 준수할 것:
-
WritableStreamDefaultWriterGetDesiredSize(writer) 값이 ≤ 0 또는 null일 때, user agent는 reader로부터 읽으면 안 됩니다.
-
reader가 BYOB reader인 경우, WritableStreamDefaultWriterGetDesiredSize(writer)를 chunk 크기 결정의 근거로 사용해야 합니다.
너무 작거나 큰 chunk를 읽는 것은 자주 비효율적입니다. 최적 chunk 크기 판단에 다른 정보도 활용할 수 있습니다.
-
backpressure 이외의 이유로 읽기/쓰기를 지연해서는 안 됩니다.
각 write가 완료될 때까지 다음 읽기/쓰기를 기다리는 구현은 이 권고를 위반합니다. 이런 구현은 dest의 내부 큐가 항상 1개 이하 chunk만 가지게 되어 쓸모없게 만듭니다.
-
-
종료 시 동작 중지: shuttingDown이 true가 되면, user agent는 더 이상 reader에서 읽기를 시작하지 말고, 이미 읽은 chunk만 아래에 따라 써야 합니다. 모든 읽기/쓰기 전에 즉시 종료 조건을 체크해야 합니다.
-
에러 및 close 전파: 아래 조건을 순서대로 적용합니다.
-
에러는 앞으로 전파: source.[[state]] 값이 "
errored
"가 되거나 이미 그렇다면,-
preventAbort가 false이면, shutdown with an action으로 ! WritableStreamAbort(dest, source.[[storedError]])와 source.[[storedError]]를 전달합니다.
-
그 외에는 shutdown으로 source.[[storedError]]를 전달합니다.
-
-
에러는 뒤로 전파: dest.[[state]] 값이 "
errored
"가 되거나 이미 그렇다면,-
preventCancel가 false이면, shutdown with an action으로 ! ReadableStreamCancel(source, dest.[[storedError]])와 dest.[[storedError]]를 전달합니다.
-
그 외에는 shutdown으로 dest.[[storedError]]를 전달합니다.
-
-
닫힘은 앞으로 전파: source.[[state]] 값이 "
closed
"가 되거나 이미 그렇다면,-
preventClose가 false이면, shutdown with an action으로 ! WritableStreamDefaultWriterCloseWithErrorPropagation(writer)를 전달합니다.
-
그 외에는 shutdown을 수행합니다.
-
-
닫힘은 뒤로 전파: ! WritableStreamCloseQueuedOrInFlight(dest) 값이 true이거나 dest.[[state]] 값이 "
closed
"라면-
Assert: 아직 읽거나 쓴 chunk가 없어야 합니다.
-
destClosed를
TypeError
로 둡니다. -
preventCancel가 false이면, shutdown with an action으로 ! ReadableStreamCancel(source, destClosed)와 destClosed를 전달합니다.
-
그 외에는 shutdown으로 destClosed를 전달합니다.
-
-
-
Shutdown with an action: 위 요구사항 중 액션 action으로 종료(옵션으로 originalError와 함께)하라고 하면 다음을 수행합니다:
-
shuttingDown이 true면 하위 단계를 중단합니다.
-
shuttingDown을 true로 설정합니다.
-
dest.[[state]]가 "
writable
"이고 ! WritableStreamCloseQueuedOrInFlight(dest) 값이 false라면 -
p를 action을 수행한 결과로 둡니다.
-
Upon fulfillment of p, finalize로 originalError를 전달합니다(있으면).
-
Upon rejection of p (newError), finalize로 newError를 전달합니다.
-
-
Shutdown: 위 요구 또는 단계에서 종료(옵션으로 error와 함께)하라고 하면 다음 수행:
-
shuttingDown이 true면 하위 단계를 중단합니다.
-
shuttingDown을 true로 설정합니다.
-
dest.[[state]]가 "
writable
"이고 ! WritableStreamCloseQueuedOrInFlight(dest) 값이 false라면 -
Finalize로 error를 전달합니다(있으면).
-
-
Finalize: 두 종료 방식 모두 결국 finalize(옵션으로 error와 함께)를 요청합니다. 아래 단계 수행:
-
! WritableStreamDefaultWriterRelease(writer)를 수행합니다.
-
reader가 구현하는
ReadableStreamBYOBReader
이면, ! ReadableStreamBYOBReaderRelease(reader)를 수행합니다. -
그 외에는 ! ReadableStreamDefaultReaderRelease(reader)를 수행합니다.
-
signal이 undefined가 아니면 remove abortAlgorithm을 signal에서 제거합니다.
-
error가 주어진 경우, promise를 error로 reject합니다.
-
그렇지 않으면, promise를 undefined로 resolve합니다.
-
-
-
promise를 반환합니다.
여기서 수행되는 다양한 추상 연산에는 객체 생성(대부분 promise)이 포함되며, 일반적으로 생성된 객체에 대한 realm을 지정해야 합니다. 하지만 locking으로 인해 이러한 객체는 저작자 코드에서 관찰될 수 없습니다. 따라서 객체를 생성할 때 사용되는 realm은 중요하지 않습니다.
두 번째 인자인 cloneForBranch2는 원본 스트림의 데이터가 반환된 두 번째 브랜치에 나타나기 전에 복제(HTML의 직렬화 가능한 객체 프레임워크 사용)를 수행할지 여부를 결정합니다. 이는 두 브랜치 모두가 서로 영향을 줄 수 있는 상황(예: transfer를 통해 chunk를 전달하는 경우 등)에서 유용합니다. 하지만 두 브랜치 간에 뚜렷한 비대칭성이 생기며, 가능한 chunk가 직렬화 가능한 것으로 제한됩니다. [HTML]
stream이 readable byte stream인 경우 cloneForBranch2는 무시되며 chunk가 항상 복제됩니다.
현행 표준에서는 ReadableStreamTee가 항상 cloneForBranch2를 false로 호출하며, 다른 명세에서는 tee 래퍼 알고리즘을 통해 true를 전달할 수 있습니다.
다음 단계를 수행합니다:
-
Assert: stream이 구현하는
ReadableStream
임을 보장합니다. -
Assert: cloneForBranch2는 boolean입니다.
-
stream.[[controller]]가 구현하는
ReadableByteStreamController
이면, ? ReadableByteStreamTee(stream)를 반환합니다. -
? ReadableStreamDefaultTee(stream, cloneForBranch2)를 반환합니다.
-
Assert: stream이 구현하는
ReadableStream
임을 보장합니다. -
Assert: cloneForBranch2는 boolean입니다.
-
reader를 ? AcquireReadableStreamDefaultReader(stream)로 둡니다.
-
reading을 false로 둡니다.
-
readAgain을 false로 둡니다.
-
canceled1을 false로 둡니다.
-
canceled2을 false로 둡니다.
-
reason1을 undefined로 둡니다.
-
reason2을 undefined로 둡니다.
-
branch1을 undefined로 둡니다.
-
branch2을 undefined로 둡니다.
-
cancelPromise를 새 promise로 둡니다.
-
pullAlgorithm을 다음 단계로 둡니다:
-
reading이 true이면,
-
readAgain을 true로 설정합니다.
-
promise resolved with undefined를 반환합니다.
-
-
reading을 true로 설정합니다.
-
readRequest를 다음 read request로 둡니다(아래 항목 포함):
- chunk steps, chunk 전달시
-
-
마이크로태스크 큐를 이용해 다음 단계 실행:
-
readAgain을 false로 설정합니다.
-
chunk1과 chunk2를 chunk로 둡니다.
-
canceled2가 false이고 cloneForBranch2가 true이면,
-
cloneResult를 StructuredClone(chunk2)로 둡니다.
-
cloneResult가 abrupt completion이면,
-
! ReadableStreamDefaultControllerError(branch1.[[controller]], cloneResult.[[Value]])를 수행합니다.
-
! ReadableStreamDefaultControllerError(branch2.[[controller]], cloneResult.[[Value]])를 수행합니다.
-
Resolve cancelPromise를 ! ReadableStreamCancel(stream, cloneResult.[[Value]])로 처리합니다.
-
Return.
-
-
그 외에는 chunk2를 cloneResult.[[Value]]로 설정합니다.
-
-
canceled1가 false이면, ! ReadableStreamDefaultControllerEnqueue(branch1.[[controller]], chunk1)를 수행합니다.
-
canceled2가 false이면, ! ReadableStreamDefaultControllerEnqueue(branch2.[[controller]], chunk2)를 수행합니다.
-
reading을 false로 설정합니다.
-
readAgain이 true이면 pullAlgorithm을 수행합니다.
-
여기서의 마이크로태스크 지연은, 아래 reader.[[closedPromise]] 처리를 통해 에러를 감지하는 데 최소 마이크로태스크가 필요하기 때문에 요구됩니다. stream의 에러가 두 브랜치 모두에 즉시 전파되길 원하므로, 비동기적으로 에러가 발생하기 전에 동기적으로 읽기가 성공하는 일이 없도록 합니다.
-
- close steps
-
-
reading을 false로 설정합니다.
-
canceled1가 false이면, ! ReadableStreamDefaultControllerClose(branch1.[[controller]])를 수행합니다.
-
canceled2가 false이면, ! ReadableStreamDefaultControllerClose(branch2.[[controller]])를 수행합니다.
-
canceled1가 false이거나 canceled2가 false이면, resolve cancelPromise를 undefined로 처리합니다.
-
- error steps
-
-
reading을 false로 설정합니다.
-
-
! ReadableStreamDefaultReaderRead(reader, readRequest)를 수행합니다.
-
promise resolved with undefined를 반환합니다.
-
-
cancel1Algorithm을 다음 단계로 둡니다(reason 인자 사용):
-
canceled1을 true로 설정합니다.
-
reason1을 reason으로 설정합니다.
-
canceled2가 true이면,
-
compositeReason를 ! CreateArrayFromList(« reason1, reason2 »)로 둡니다.
-
cancelResult를 ! ReadableStreamCancel(stream, compositeReason)로 둡니다.
-
Resolve cancelPromise를 cancelResult로 처리합니다.
-
-
cancelPromise를 반환합니다.
-
-
cancel2Algorithm을 다음 단계로 둡니다(reason 인자 사용):
-
canceled2을 true로 설정합니다.
-
reason2을 reason으로 설정합니다.
-
canceled1가 true이면,
-
compositeReason를 ! CreateArrayFromList(« reason1, reason2 »)로 둡니다.
-
cancelResult를 ! ReadableStreamCancel(stream, compositeReason)로 둡니다.
-
Resolve cancelPromise를 cancelResult로 처리합니다.
-
-
cancelPromise를 반환합니다.
-
-
startAlgorithm을 undefined를 반환하는 알고리즘으로 둡니다.
-
branch1을 ! CreateReadableStream(startAlgorithm, pullAlgorithm, cancel1Algorithm)로 설정합니다.
-
branch2을 ! CreateReadableStream(startAlgorithm, pullAlgorithm, cancel2Algorithm)로 설정합니다.
-
upon rejection of reader.[[closedPromise]] with reason r:
-
! ReadableStreamDefaultControllerError(branch1.[[controller]], r)를 수행합니다.
-
! ReadableStreamDefaultControllerError(branch2.[[controller]], r)를 수행합니다.
-
canceled1가 false이거나 canceled2가 false이면, resolve cancelPromise를 undefined로 처리합니다.
-
-
« branch1, branch2 »를 반환합니다.
-
Assert: stream이 구현하는
ReadableStream
임을 보장합니다. -
Assert: stream.[[controller]]가 구현하는
ReadableByteStreamController
임을 보장합니다. -
reader를 ? AcquireReadableStreamDefaultReader(stream)로 둡니다.
-
reading을 false로 둡니다.
-
readAgainForBranch1을 false로 둡니다.
-
readAgainForBranch2을 false로 둡니다.
-
canceled1을 false로 둡니다.
-
canceled2을 false로 둡니다.
-
reason1을 undefined로 둡니다.
-
reason2을 undefined로 둡니다.
-
branch1을 undefined로 둡니다.
-
branch2을 undefined로 둡니다.
-
cancelPromise를 새 promise로 둡니다.
-
forwardReaderError를 다음 단계로 둡니다(thisReader 인자 사용):
-
upon rejection of thisReader.[[closedPromise]] with reason r:
-
thisReader가 reader가 아니라면 return.
-
! ReadableByteStreamControllerError(branch1.[[controller]], r)를 수행합니다.
-
! ReadableByteStreamControllerError(branch2.[[controller]], r)를 수행합니다.
-
canceled1가 false이거나 canceled2가 false이면 resolve cancelPromise를 undefined로 처리합니다.
-
-
-
pullWithDefaultReader를 다음 단계로 둡니다:
-
reader가 구현하는
ReadableStreamBYOBReader
이면,-
Assert: reader.[[readIntoRequests]]가 비어 있음을 보장합니다.
-
! ReadableStreamBYOBReaderRelease(reader)를 수행합니다.
-
reader를 ! AcquireReadableStreamDefaultReader(stream)로 둡니다.
-
forwardReaderError를 reader에 대해 수행합니다.
-
-
readRequest를 다음 read request로 둡니다(아래 항목 포함):
- chunk steps, chunk 전달시
-
-
마이크로태스크 큐를 이용해 다음 단계 실행:
-
readAgainForBranch1을 false로 설정합니다.
-
readAgainForBranch2을 false로 설정합니다.
-
chunk1과 chunk2를 chunk로 둡니다.
-
canceled1가 false이고 canceled2가 false이면,
-
cloneResult를 CloneAsUint8Array(chunk)로 둡니다.
-
cloneResult가 abrupt completion이면,
-
! ReadableByteStreamControllerError(branch1.[[controller]], cloneResult.[[Value]])를 수행합니다.
-
! ReadableByteStreamControllerError(branch2.[[controller]], cloneResult.[[Value]])를 수행합니다.
-
Resolve cancelPromise를 ! ReadableStreamCancel(stream, cloneResult.[[Value]])로 처리합니다.
-
Return.
-
-
그 외에는 chunk2를 cloneResult.[[Value]]로 설정합니다.
-
-
canceled1가 false이면, ! ReadableByteStreamControllerEnqueue(branch1.[[controller]], chunk1)를 수행합니다.
-
canceled2가 false이면, ! ReadableByteStreamControllerEnqueue(branch2.[[controller]], chunk2)를 수행합니다.
-
reading을 false로 설정합니다.
-
readAgainForBranch1이 true이면 pull1Algorithm을 수행합니다.
-
그 외에는 readAgainForBranch2가 true이면 pull2Algorithm을 수행합니다.
-
여기서의 마이크로태스크 지연은, 아래 reader.[[closedPromise]] 처리를 통해 에러를 감지하는 데 최소 마이크로태스크가 필요하기 때문에 요구됩니다. stream의 에러가 두 브랜치 모두에 즉시 전파되길 원하므로, 비동기적으로 에러가 발생하기 전에 동기적으로 읽기가 성공하는 일이 없도록 합니다.
-
- close steps
-
-
reading을 false로 설정합니다.
-
canceled1가 false이면, ! ReadableByteStreamControllerClose(branch1.[[controller]])를 수행합니다.
-
canceled2가 false이면, ! ReadableByteStreamControllerClose(branch2.[[controller]])를 수행합니다.
-
branch1.[[controller]].[[pendingPullIntos]] 가 비어 있지 않으면, ! ReadableByteStreamControllerRespond(branch1.[[controller]], 0)을 수행합니다.
-
branch2.[[controller]].[[pendingPullIntos]] 가 비어 있지 않으면, ! ReadableByteStreamControllerRespond(branch2.[[controller]], 0)을 수행합니다.
-
canceled1가 false이거나 canceled2가 false이면 resolve cancelPromise를 undefined로 처리합니다.
-
- error steps
-
-
reading을 false로 설정합니다.
-
-
! ReadableStreamDefaultReaderRead(reader, readRequest)를 수행합니다.
-
-
pullWithBYOBReader를 다음 단계로 둡니다(view, forBranch2 인자 사용):
-
reader가 구현하는
ReadableStreamDefaultReader
이면,-
Assert: reader.[[readRequests]]가 비어 있음을 보장합니다.
-
! ReadableStreamDefaultReaderRelease(reader)를 수행합니다.
-
reader를 ! AcquireReadableStreamBYOBReader(stream)로 둡니다.
-
forwardReaderError를 reader에 대해 수행합니다.
-
-
byobBranch를 forBranch2가 true이면 branch2, 아니면 branch1로 둡니다.
-
otherBranch를 forBranch2가 false이면 branch2, 아니면 branch1로 둡니다.
-
readIntoRequest를 다음 read-into request로 둡니다(아래 항목 포함):
- chunk steps, chunk 전달시
-
-
마이크로태스크 큐를 이용해 다음 단계 실행:
-
readAgainForBranch1을 false로 설정합니다.
-
readAgainForBranch2을 false로 설정합니다.
-
byobCanceled를 forBranch2가 true이면 canceled2, 아니면 canceled1로 둡니다.
-
otherCanceled를 forBranch2가 false이면 canceled2, 아니면 canceled1로 둡니다.
-
otherCanceled가 false이면,
-
cloneResult를 CloneAsUint8Array(chunk)로 둡니다.
-
cloneResult가 abrupt completion이면,
-
! ReadableByteStreamControllerError(byobBranch.[[controller]], cloneResult.[[Value]])를 수행합니다.
-
! ReadableByteStreamControllerError(otherBranch.[[controller]], cloneResult.[[Value]])를 수행합니다.
-
Resolve cancelPromise를 ! ReadableStreamCancel(stream, cloneResult.[[Value]])로 처리합니다.
-
Return.
-
-
그 외에는 clonedChunk를 cloneResult.[[Value]]로 둡니다.
-
byobCanceled가 false이면, ! ReadableByteStreamControllerRespondWithNewView(byobBranch.[[controller]], chunk)를 수행합니다.
-
! ReadableByteStreamControllerEnqueue(otherBranch.[[controller]], clonedChunk)를 수행합니다.
-
-
그 외에는 byobCanceled가 false이면, ! ReadableByteStreamControllerRespondWithNewView(byobBranch.[[controller]], chunk)를 수행합니다.
-
reading을 false로 설정합니다.
-
readAgainForBranch1이 true이면 pull1Algorithm을 수행합니다.
-
그 외에는 readAgainForBranch2가 true이면 pull2Algorithm을 수행합니다.
-
여기서의 마이크로태스크 지연은, 아래 reader.[[closedPromise]] 처리를 통해 에러를 감지하는 데 최소 마이크로태스크가 필요하기 때문에 요구됩니다. stream의 에러가 두 브랜치 모두에 즉시 전파되길 원하므로, 비동기적으로 에러가 발생하기 전에 동기적으로 읽기가 성공하는 일이 없도록 합니다.
-
- close steps, chunk 전달시
-
-
reading을 false로 설정합니다.
-
byobCanceled를 forBranch2가 true이면 canceled2, 아니면 canceled1로 둡니다.
-
otherCanceled를 forBranch2가 false이면 canceled2, 아니면 canceled1로 둡니다.
-
byobCanceled가 false이면, ! ReadableByteStreamControllerClose(byobBranch.[[controller]])를 수행합니다.
-
otherCanceled가 false이면, ! ReadableByteStreamControllerClose(otherBranch.[[controller]])를 수행합니다.
-
chunk가 undefined가 아니면,
-
Assert: chunk.[[ByteLength]]가 0임을 보장합니다.
-
byobCanceled가 false이면, ! ReadableByteStreamControllerRespondWithNewView(byobBranch.[[controller]], chunk)를 수행합니다.
-
otherCanceled가 false이고 otherBranch.[[controller]].[[pendingPullIntos]] 가 비어 있지 않으면, ! ReadableByteStreamControllerRespond(otherBranch.[[controller]], 0)을 수행합니다.
-
-
byobCanceled가 false이거나 otherCanceled가 false이면 resolve cancelPromise를 undefined로 처리합니다.
-
- error steps
-
-
reading을 false로 설정합니다.
-
-
! ReadableStreamBYOBReaderRead(reader, view, 1, readIntoRequest)를 수행합니다.
-
-
pull1Algorithm을 다음 단계로 둡니다:
-
reading이 true이면,
-
readAgainForBranch1을 true로 설정합니다.
-
promise resolved with undefined를 반환합니다.
-
-
reading을 true로 설정합니다.
-
byobRequest를 ! ReadableByteStreamControllerGetBYOBRequest(branch1.[[controller]])로 둡니다.
-
byobRequest가 null이면 pullWithDefaultReader를 수행합니다.
-
그 외에는 pullWithBYOBReader를 byobRequest.[[view]]와 false 인자를 전달하여 수행합니다.
-
promise resolved with undefined를 반환합니다.
-
-
pull2Algorithm을 다음 단계로 둡니다:
-
reading이 true이면,
-
readAgainForBranch2을 true로 설정합니다.
-
promise resolved with undefined를 반환합니다.
-
-
reading을 true로 설정합니다.
-
byobRequest를 ! ReadableByteStreamControllerGetBYOBRequest(branch2.[[controller]])로 둡니다.
-
byobRequest가 null이면 pullWithDefaultReader를 수행합니다.
-
그 외에는 pullWithBYOBReader를 byobRequest.[[view]]와 true 인자를 전달하여 수행합니다.
-
promise resolved with undefined를 반환합니다.
-
-
cancel1Algorithm을 다음 단계로 둡니다(reason 인자 사용):
-
canceled1을 true로 설정합니다.
-
reason1을 reason으로 설정합니다.
-
canceled2가 true이면,
-
compositeReason를 ! CreateArrayFromList(« reason1, reason2 »)로 둡니다.
-
cancelResult를 ! ReadableStreamCancel(stream, compositeReason)로 둡니다.
-
Resolve cancelPromise를 cancelResult로 처리합니다.
-
-
cancelPromise를 반환합니다.
-
-
cancel2Algorithm을 다음 단계로 둡니다(reason 인자 사용):
-
canceled2을 true로 설정합니다.
-
reason2을 reason으로 설정합니다.
-
canceled1가 true이면,
-
compositeReason를 ! CreateArrayFromList(« reason1, reason2 »)로 둡니다.
-
cancelResult를 ! ReadableStreamCancel(stream, compositeReason)로 둡니다.
-
Resolve cancelPromise를 cancelResult로 처리합니다.
-
-
cancelPromise를 반환합니다.
-
-
startAlgorithm을 undefined를 반환하는 알고리즘으로 둡니다.
-
branch1을 ! CreateReadableByteStream(startAlgorithm, pull1Algorithm, cancel1Algorithm)로 설정합니다.
-
branch2을 ! CreateReadableByteStream(startAlgorithm, pull2Algorithm, cancel2Algorithm)로 설정합니다.
-
reader를 인자로 하여 forwardReaderError를 수행합니다.
-
« branch1, branch2 »를 반환합니다.
4.9.2. 컨트롤러와의 인터페이스
명세 구조상, ReadableStream
클래스가 단순 readable stream과 바이트 스트림을 읽을 수 있는 스트림의 동작을 하나의 클래스에 캡슐화하는 방식은, 잠재적으로 다른 논리를 두
컨트롤러 클래스,
ReadableStreamDefaultController
및 ReadableByteStreamController
내부에 대부분 집중시키는 것입니다. 이 클래스들은 스트림의 내부 큐가 어떻게 관리되는지와 underlying source 또는 underlying byte source와 어떻게
인터페이스하는지에 대한 대부분의 상태 저장 슬롯과 추상 연산을 정의합니다.
각 컨트롤러 클래스는 ReadableStream
알고리즘에 의해 호출되는 세 가지 내부 메서드를 정의합니다:
- [[CancelSteps]](reason)
- 스트림이 취소될 때 실행되는 컨트롤러의 단계로, 컨트롤러에 저장된 상태를 정리하고 underlying source에 알립니다.
- [[PullSteps]](readRequest)
- default reader에서 읽기가 발생할 때 실행되는 컨트롤러의 단계로, 컨트롤러에 큐된 chunk를 pull하거나 underlying source에서 더 많은 chunk를 pull합니다.
- [[ReleaseSteps]]()
- reader가 해제될 때 실행되는 컨트롤러의 단계로, 컨트롤러에 저장된 reader 관련 리소스를 정리합니다.
(이 메서드들은 내부 메서드로 정의되어, 어떤 타입의 컨트롤러가 있는지 분기하지 않고 ReadableStream
알고리즘에서 다형적으로 호출할 수 있게 합니다.)
이 절의 나머지는 컨트롤러 구현체가 연관된 ReadableStream
객체에 영향을 미치기 위해 사용하는 추상 연산에 관한 것입니다. 이는 컨트롤러의 내부 상태 변화를 ReadableStream
의
공개 API를 통해 개발자가 볼 수 있는 결과로 변환합니다.
-
Assert: stream.[[reader]]가 구현하는
ReadableStreamBYOBReader
임을 보장합니다. -
Assert: stream.[[state]]가 "
readable
" 또는 "closed
"임을 보장합니다. -
Append readRequest를 stream.[[reader]].[[readIntoRequests]]에 추가합니다.
-
Assert: stream.[[reader]]가 구현하는
ReadableStreamDefaultReader
임을 보장합니다. -
Assert: stream.[[state]]가 "
readable
"임을 보장합니다. -
Append readRequest를 stream.[[reader]].[[readRequests]]에 추가합니다.
-
stream.[[disturbed]]를 true로 설정합니다.
-
stream.[[state]]가 "
closed
"라면 promise resolved with undefined를 반환합니다. -
stream.[[state]]가 "
errored
"라면 promise rejected with stream.[[storedError]]를 반환합니다. -
! ReadableStreamClose(stream)을 수행합니다.
-
reader를 stream.[[reader]]로 둡니다.
-
reader가 undefined가 아니고 reader가 구현하는
ReadableStreamBYOBReader
라면,-
readIntoRequests를 reader.[[readIntoRequests]]로 둡니다.
-
reader.[[readIntoRequests]]를 빈 리스트로 설정합니다.
-
각 readIntoRequest에 대해, readIntoRequests의 요소,
-
readIntoRequest의 close steps를 undefined로 실행합니다.
-
-
-
sourceCancelPromise를 ! stream.[[controller]].[[CancelSteps]](reason)로 둡니다.
-
reacting을 통해 sourceCancelPromise의 이행 단계에서 undefined를 반환합니다.
-
Assert: stream.[[state]]가 "
readable
"임을 보장합니다. -
stream.[[state]]를 "
closed
"로 설정합니다. -
reader를 stream.[[reader]]로 둡니다.
-
reader가 undefined라면 반환합니다.
-
Resolve reader.[[closedPromise]]에 undefined를 전달합니다.
-
reader가 구현하는
ReadableStreamDefaultReader
라면,-
readRequests를 reader.[[readRequests]]로 둡니다.
-
reader.[[readRequests]]를 빈 리스트로 설정합니다.
-
각 readRequest에 대해, readRequests의 요소,
-
readRequest의 close steps를 실행합니다.
-
-
-
Assert: stream.[[state]]가 "
readable
"임을 보장합니다. -
stream.[[state]]를 "
errored
"로 설정합니다. -
stream.[[storedError]]를 e로 설정합니다.
-
reader를 stream.[[reader]]로 둡니다.
-
reader가 undefined라면 반환합니다.
-
Reject reader.[[closedPromise]]에 e를 전달합니다.
-
reader.[[closedPromise]].
[[PromiseIsHandled]]
를 true로 설정합니다. -
reader가 구현하는
ReadableStreamDefaultReader
라면,-
! ReadableStreamDefaultReaderErrorReadRequests(reader, e)를 수행합니다.
-
-
그 외에는,
-
Assert: reader가 구현하는
ReadableStreamBYOBReader
임을 보장합니다. -
! ReadableStreamBYOBReaderErrorReadIntoRequests(reader, e)를 수행합니다.
-
-
Assert: ! ReadableStreamHasBYOBReader(stream) 가 true임을 보장합니다.
-
reader를 stream.[[reader]]로 둡니다.
-
Assert: reader.[[readIntoRequests]]가 비어 있지 않음을 보장합니다.
-
readIntoRequest를 reader.[[readIntoRequests]][0]로 둡니다.
-
Remove readIntoRequest를 reader.[[readIntoRequests]]에서 제거합니다.
-
done이 true라면 readIntoRequest의 close steps를 chunk로 실행합니다.
-
그 외에는 readIntoRequest의 chunk steps를 chunk로 실행합니다.
-
Assert: ! ReadableStreamHasDefaultReader(stream) 가 true임을 보장합니다.
-
reader를 stream.[[reader]]로 둡니다.
-
Assert: reader.[[readRequests]]가 비어 있지 않음을 보장합니다.
-
readRequest를 reader.[[readRequests]][0]로 둡니다.
-
Remove readRequest를 reader.[[readRequests]]에서 제거합니다.
-
done이 true라면 readRequest의 close steps를 실행합니다.
-
그 외에는 readRequest의 chunk steps를 chunk로 실행합니다.
-
Assert: ! ReadableStreamHasBYOBReader(stream) 가 true임을 보장합니다.
-
stream.[[reader]].[[readIntoRequests]]의 크기를 반환합니다.
-
Assert: ! ReadableStreamHasDefaultReader(stream) 가 true임을 보장합니다.
-
stream.[[reader]].[[readRequests]]의 크기를 반환합니다.
-
reader를 stream.[[reader]]로 둡니다.
-
reader가 undefined라면 false를 반환합니다.
-
reader가 구현하는
ReadableStreamBYOBReader
라면 true를 반환합니다. -
false를 반환합니다.
-
reader를 stream.[[reader]]로 둡니다.
-
reader가 undefined라면 false를 반환합니다.
-
reader가 구현하는
ReadableStreamDefaultReader
라면 true를 반환합니다. -
false를 반환합니다.
4.9.3. 리더
다음 추상 연산은 ReadableStreamDefaultReader
및 ReadableStreamBYOBReader
인스턴스의 구현과 조작을 지원합니다.
-
stream을 reader.[[stream]]로 둡니다.
-
Assert: stream이 undefined가 아님을 보장합니다.
-
! ReadableStreamCancel(stream, reason)를 반환합니다.
-
reader.[[stream]]을 stream으로 설정합니다.
-
stream.[[reader]]를 reader로 설정합니다.
-
stream.[[state]]가 "
readable
"이면,-
reader.[[closedPromise]]를 새 promise로 설정합니다.
-
-
그 외에, stream.[[state]]가 "
closed
"이면,-
reader.[[closedPromise]]를 promise resolved with undefined로 설정합니다.
-
-
그 외에는,
-
Assert: stream.[[state]]가 "
errored
"임을 보장합니다. -
reader.[[closedPromise]]를 promise rejected with stream.[[storedError]]로 설정합니다.
-
reader.[[closedPromise]].
[[PromiseIsHandled]]
를 true로 설정합니다.
-
-
stream을 reader.[[stream]]로 둡니다.
-
Assert: stream이 undefined가 아님을 보장합니다.
-
Assert: stream.[[reader]]가 reader임을 보장합니다.
-
stream.[[state]]가 "
readable
"이면, reject reader.[[closedPromise]]에TypeError
예외를 전달합니다. -
그 외에는 reader.[[closedPromise]]를 promise rejected with
TypeError
예외로 설정합니다. -
reader.[[closedPromise]].
[[PromiseIsHandled]]
를 true로 설정합니다. -
! stream.[[controller]].[[ReleaseSteps]]()를 수행합니다.
-
stream.[[reader]]를 undefined로 설정합니다.
-
reader.[[stream]]를 undefined로 설정합니다.
-
readIntoRequests를 reader.[[readIntoRequests]]로 둡니다.
-
reader.[[readIntoRequests]]를 새 빈 리스트로 설정합니다.
-
각 readIntoRequest에 대해, readIntoRequests의 요소,
-
readIntoRequest의 error steps를 e로 실행합니다.
-
-
stream을 reader.[[stream]]로 둡니다.
-
Assert: stream이 undefined가 아님을 보장합니다.
-
stream.[[disturbed]]를 true로 설정합니다.
-
stream.[[state]]가 "
errored
"이면, readIntoRequest의 error steps를 stream.[[storedError]]로 실행합니다. -
그 외에는 ! ReadableByteStreamControllerPullInto(stream.[[controller]], view, min, readIntoRequest)를 수행합니다.
-
! ReadableStreamReaderGenericRelease(reader)를 수행합니다.
-
e를
TypeError
예외로 둡니다. -
! ReadableStreamBYOBReaderErrorReadIntoRequests(reader, e)를 수행합니다.
-
readRequests를 reader.[[readRequests]]로 둡니다.
-
reader.[[readRequests]]를 새 빈 리스트로 설정합니다.
-
각 readRequest에 대해, readRequests의 요소,
-
readRequest의 error steps를 e로 실행합니다.
-
-
stream을 reader.[[stream]]로 둡니다.
-
Assert: stream이 undefined가 아님을 보장합니다.
-
stream.[[disturbed]]를 true로 설정합니다.
-
stream.[[state]]가 "
closed
"이면, readRequest의 close steps를 실행합니다. -
그 외에, stream.[[state]]가 "
errored
"이면, readRequest의 error steps를 stream.[[storedError]]로 실행합니다. -
그 외에는,
-
Assert: stream.[[state]]가 "
readable
"임을 보장합니다. -
! stream.[[controller]].[[PullSteps]](readRequest)를 수행합니다.
-
-
! ReadableStreamReaderGenericRelease(reader)를 수행합니다.
-
e를
TypeError
예외로 둡니다. -
! ReadableStreamDefaultReaderErrorReadRequests(reader, e)를 수행합니다.
-
! IsReadableStreamLocked(stream)가 true면,
TypeError
예외를 throw합니다. -
stream.[[controller]]가 구현하는
ReadableByteStreamController
가 아니면,TypeError
예외를 throw합니다. -
! ReadableStreamReaderGenericInitialize(reader, stream)를 수행합니다.
-
reader.[[readIntoRequests]]를 새 빈 리스트로 설정합니다.
-
! IsReadableStreamLocked(stream)가 true면,
TypeError
예외를 throw합니다. -
! ReadableStreamReaderGenericInitialize(reader, stream)를 수행합니다.
-
reader.[[readRequests]]를 새 빈 리스트로 설정합니다.
4.9.4. 기본 컨트롤러
다음 추상 연산은
ReadableStreamDefaultController
클래스의 구현을 지원합니다.
-
shouldPull을 ! ReadableStreamDefaultControllerShouldCallPull(controller)로 둡니다.
-
shouldPull이 false면, 반환합니다.
-
controller.[[pulling]]이 true라면,
-
controller.[[pullAgain]]을 true로 설정합니다.
-
반환합니다.
-
-
Assert: controller.[[pullAgain]]이 false임을 보장합니다.
-
controller.[[pulling]]을 true로 설정합니다.
-
pullPromise를 controller.[[pullAlgorithm]]의 실행 결과로 둡니다.
-
pullPromise가 이행될 때,
-
controller.[[pulling]]을 false로 설정합니다.
-
controller.[[pullAgain]]이 true라면,
-
controller.[[pullAgain]]을 false로 설정합니다.
-
! ReadableStreamDefaultControllerCallPullIfNeeded(controller)를 수행합니다.
-
-
-
pullPromise가 거부될 때, reason e로,
-
! ReadableStreamDefaultControllerError(controller, e)를 수행합니다.
-
-
stream을 controller.[[stream]]로 둡니다.
-
! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) 가 false이면 false를 반환합니다.
-
controller.[[started]]가 false이면 false를 반환합니다.
-
! IsReadableStreamLocked(stream)이 true이고, !ReadableStreamGetNumReadRequests(stream) > 0이면 true를 반환합니다.
-
desiredSize를 ! ReadableStreamDefaultControllerGetDesiredSize(controller)로 둡니다.
-
Assert: desiredSize가 null이 아님을 보장합니다.
-
desiredSize > 0이면 true를 반환합니다.
-
false를 반환합니다.
ReadableStream
이
참조되고 있더라도
가비지 콜렉션될 수 있습니다.
이것은 약한 참조(weak references)를 통해 관찰할 수 있습니다. 자세한 내용은 tc39/proposal-weakrefs#31를 참고하세요.
다음 단계를 수행합니다:
-
controller.[[pullAlgorithm]]을 undefined로 설정합니다.
-
controller.[[cancelAlgorithm]]을 undefined로 설정합니다.
-
controller.[[strategySizeAlgorithm]] 을 undefined로 설정합니다.
-
! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) 가 false이면 반환합니다.
-
stream을 controller.[[stream]]로 둡니다.
-
controller.[[closeRequested]]를 true로 설정합니다.
-
-
! ReadableStreamDefaultControllerClearAlgorithms(controller)를 수행합니다.
-
! ReadableStreamClose(stream)를 수행합니다.
-
-
! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) 가 false이면 반환합니다.
-
stream을 controller.[[stream]]로 둡니다.
-
! IsReadableStreamLocked(stream)이 true이고, !ReadableStreamGetNumReadRequests(stream) > 0이면, !ReadableStreamFulfillReadRequest(stream, chunk, false)를 수행합니다.
-
그 외에는,
-
result를 controller.[[strategySizeAlgorithm]]을 chunk에 전달하여 실행한 결과로 둡니다. 결과를 completion record로 해석합니다.
-
result가 abrupt completion이면,
-
! ReadableStreamDefaultControllerError(controller, result.[[Value]])를 수행합니다.
-
result를 반환합니다.
-
-
chunkSize를 result.[[Value]]로 둡니다.
-
enqueueResult를 EnqueueValueWithSize(controller, chunk, chunkSize)의 실행 결과로 둡니다.
-
enqueueResult가 abrupt completion이면,
-
! ReadableStreamDefaultControllerError(controller, enqueueResult.[[Value]])를 수행합니다.
-
enqueueResult를 반환합니다.
-
-
-
! ReadableStreamDefaultControllerCallPullIfNeeded(controller)를 수행합니다.
-
stream을 controller.[[stream]]로 둡니다.
-
stream.[[state]]가 "
readable
"가 아니면 반환합니다. -
! ResetQueue(controller)를 수행합니다.
-
! ReadableStreamDefaultControllerClearAlgorithms(controller)를 수행합니다.
-
! ReadableStreamError(stream, e)를 수행합니다.
-
state를 controller.[[stream]].[[state]]로 둡니다.
-
state가 "
errored
"면 null을 반환합니다. -
state가 "
closed
"면 0을 반환합니다. -
controller.[[strategyHWM]] − controller.[[queueTotalSize]]를 반환합니다.
TransformStream
구현에서 사용됩니다.
다음 단계를 수행합니다:
-
! ReadableStreamDefaultControllerShouldCallPull(controller) 가 true이면 false를 반환합니다.
-
그 외에는 true를 반환합니다.
-
state를 controller.[[stream]].[[state]]로 둡니다.
-
controller.[[closeRequested]]가 false이고 state가 "
readable
"이면 true를 반환합니다. -
그 외에는 false를 반환합니다.
controller.[[closeRequested]]가 false이지만
state가 "readable
"가 아닌 경우는,
controller.error()
를
통해 스트림에 오류가 발생했거나,
컨트롤러의 controller.close()
메서드가 호출되지 않은 상태에서 스트림이 닫힌 경우(예: stream.cancel()
호출 등)입니다.
-
Assert: stream.[[controller]]가 undefined임을 보장합니다.
-
controller.[[stream]]을 stream으로 설정합니다.
-
! ResetQueue(controller)를 수행합니다.
-
controller.[[started]], controller.[[closeRequested]], controller.[[pullAgain]], controller.[[pulling]]을 모두 false로 설정합니다.
-
controller.[[strategySizeAlgorithm]] 을 sizeAlgorithm으로, controller.[[strategyHWM]]을 highWaterMark로 설정합니다.
-
controller.[[pullAlgorithm]]을 pullAlgorithm으로 설정합니다.
-
controller.[[cancelAlgorithm]]을 cancelAlgorithm으로 설정합니다.
-
stream.[[controller]]를 controller로 설정합니다.
-
startResult를 startAlgorithm을 실행한 결과로 둡니다. (예외를 발생시킬 수 있음)
-
startPromise를 promise resolved with startResult로 둡니다.
-
startPromise가 이행될 때,
-
controller.[[started]]를 true로 설정합니다.
-
Assert: controller.[[pulling]]이 false임을 보장합니다.
-
Assert: controller.[[pullAgain]]이 false임을 보장합니다.
-
! ReadableStreamDefaultControllerCallPullIfNeeded(controller)를 수행합니다.
-
-
startPromise가 거부될 때, reason r로,
-
! ReadableStreamDefaultControllerError(controller, r)를 수행합니다.
-
-
controller를 new
ReadableStreamDefaultController
로 둡니다. -
startAlgorithm을 undefined를 반환하는 알고리즘으로 둡니다.
-
pullAlgorithm을 promise resolved with undefined를 반환하는 알고리즘으로 둡니다.
-
cancelAlgorithm을 promise resolved with undefined를 반환하는 알고리즘으로 둡니다.
-
underlyingSourceDict["
start
"] exists하면, startAlgorithm을 invoke를 통해 underlyingSourceDict["start
"] 에 인자 « controller »와 callback this value underlyingSource를 전달하여 실행 결과를 반환하는 알고리즘으로 설정합니다. -
underlyingSourceDict["
pull
"] exists하면, pullAlgorithm을 invoke를 통해 underlyingSourceDict["pull
"] 에 인자 « controller »와 callback this value underlyingSource를 전달하여 실행 결과를 반환하는 알고리즘으로 설정합니다. -
underlyingSourceDict["
cancel
"] exists하면, cancelAlgorithm을 reason 인자를 받아, invoke를 통해 underlyingSourceDict["cancel
"] 에 인자 « reason »와 callback this value underlyingSource를 전달하여 실행 결과를 반환하는 알고리즘으로 설정합니다. -
? SetUpReadableStreamDefaultController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm)를 수행합니다.
4.9.5. 바이트 스트림 컨트롤러
-
shouldPull을 ! ReadableByteStreamControllerShouldCallPull(controller)의 값으로 둔다.
-
shouldPull이 false이면, 반환한다.
-
controller.[[pulling]]이 true라면,
-
controller.[[pullAgain]]을 true로 설정한다.
-
반환한다.
-
-
단언: controller.[[pullAgain]]이 false이다.
-
controller.[[pulling]]을 true로 설정한다.
-
pullPromise를 controller.[[pullAlgorithm]]을 수행한 결과로 둔다.
-
이행 시 pullPromise에 대해,
-
controller.[[pulling]]을 false로 설정한다.
-
controller.[[pullAgain]]이 true라면,
-
controller.[[pullAgain]]을 false로 설정한다.
-
! ReadableByteStreamControllerCallPullIfNeeded(controller)를 수행한다.
-
-
-
거부 시 pullPromise에 대해, 이유 e와 함께,
-
! ReadableByteStreamControllerError(controller, e)를 수행한다.
-
ReadableStream
자체가 여전히 참조되고 있더라도 가비지 컬렉션될 수 있도록 한다.
이는 약한 참조를 통해 관측할 수 있다. 자세한 내용은 tc39/proposal-weakrefs#31를 참고하라.
다음 단계들을 수행한다:
-
controller.[[pullAlgorithm]]을 undefined로 설정한다.
-
controller.[[cancelAlgorithm]]을 undefined로 설정한다.
-
! ReadableByteStreamControllerInvalidateBYOBRequest(controller)를 수행한다.
-
controller.[[pendingPullIntos]]를 새로운 빈 리스트로 설정한다.
-
stream을 controller.[[stream]]의 값으로 둔다.
-
controller.[[closeRequested]]가 true이거나 stream.[[state]]가 "
readable
"이 아니라면, 반환한다. -
controller.[[queueTotalSize]] > 0이라면,
-
controller.[[closeRequested]]를 true로 설정한다.
-
반환한다.
-
-
controller.[[pendingPullIntos]]가 비어 있지 않다면,
-
firstPendingPullInto를 controller.[[pendingPullIntos]][0]의 값으로 둔다.
-
firstPendingPullInto의 bytes filled 값을 firstPendingPullInto의 element size로 나눈 나머지가 0이 아니라면,
-
e를 새로운
TypeError
예외로 둔다. -
! ReadableByteStreamControllerError(controller, e)를 수행한다.
-
e를 throw 한다.
-
-
-
! ReadableByteStreamControllerClearAlgorithms(controller)를 수행한다.
-
! ReadableStreamClose(stream)를 수행한다.
-
단언: stream.[[state]]가 "
errored
"가 아니다. -
단언: pullIntoDescriptor.reader type이 "
none
"가 아니다. -
done을 false로 둔다.
-
stream.[[state]]가 "
closed
"라면,-
단언: pullIntoDescriptor의 bytes filled 값을 pullIntoDescriptor의 element size로 나눈 나머지가 0이다.
-
done을 true로 설정한다.
-
-
filledView를 ! ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor)의 값으로 둔다.
-
pullIntoDescriptor의 reader type이 "
default
"라면,-
! ReadableStreamFulfillReadRequest(stream, filledView, done)를 수행한다.
-
-
그 밖의 경우,
-
단언: pullIntoDescriptor의 reader type이 "
byob
"이다. -
! ReadableStreamFulfillReadIntoRequest(stream, filledView, done)를 수행한다.
-
-
bytesFilled을 pullIntoDescriptor의 bytes filled 값으로 둔다.
-
elementSize를 pullIntoDescriptor의 element size 값으로 둔다.
-
단언: bytesFilled ≤ pullIntoDescriptor의 byte length 값이다.
-
단언: bytesFilled을 elementSize로 나눈 나머지가 0이다.
-
buffer를 ! TransferArrayBuffer(pullIntoDescriptor의 buffer)의 값으로 둔다.
-
! Construct(pullIntoDescriptor의 view constructor, « buffer, pullIntoDescriptor의 byte offset, bytesFilled ÷ elementSize »)를 반환한다.
-
stream을 controller.[[stream]]의 값으로 둔다.
-
controller.[[closeRequested]]가 true이거나 stream.[[state]]가 "
readable
"이 아니라면, 반환한다. -
buffer를 chunk.[[ViewedArrayBuffer]]의 값으로 둔다.
-
byteOffset을 chunk.[[ByteOffset]]의 값으로 둔다.
-
byteLength를 chunk.[[ByteLength]]의 값으로 둔다.
-
! IsDetachedBuffer(buffer)가 true라면,
TypeError
예외를 throw 한다. -
transferredBuffer를 ? TransferArrayBuffer(buffer)의 값으로 둔다.
-
controller.[[pendingPullIntos]]가 비어있지 않다면,
-
firstPendingPullInto를 controller.[[pendingPullIntos]][0]의 값으로 둔다.
-
! IsDetachedBuffer(firstPendingPullInto의 buffer) 가 true라면
TypeError
예외를 throw 한다. -
! ReadableByteStreamControllerInvalidateBYOBRequest(controller)를 수행한다.
-
firstPendingPullInto의 buffer를 ! TransferArrayBuffer(firstPendingPullInto의 buffer)로 설정한다.
-
firstPendingPullInto의 reader type이 "
none
"이라면, ? ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(controller, firstPendingPullInto)를 수행한다.
-
-
! ReadableStreamHasDefaultReader(stream) 가 true라면,
-
! ReadableByteStreamControllerProcessReadRequestsUsingQueue(controller)를 수행한다.
-
! ReadableStreamGetNumReadRequests(stream) 가 0이라면,
-
단언: controller.[[pendingPullIntos]] 가 비어있다.
-
! ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength)를 수행한다.
-
-
그 밖의 경우,
-
controller.[[pendingPullIntos]] 가 비어있지 않다면,
-
단언: controller.[[pendingPullIntos]][0]의 reader type이 "
default
"이다. -
! ReadableByteStreamControllerShiftPendingPullInto(controller)를 수행한다.
-
-
transferredView를 ! Construct(
%Uint8Array%
, « transferredBuffer, byteOffset, byteLength »)로 둔다. -
! ReadableStreamFulfillReadRequest(stream, transferredView, false)를 수행한다.
-
-
그 밖의 경우, ! ReadableStreamHasBYOBReader(stream) 가 true라면,
-
! ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength)를 수행한다.
-
filledPullIntos를 ! ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller)의 결과로 둔다.
-
각 filledPullInto를 filledPullIntos에서,
-
! ReadableByteStreamControllerCommitPullIntoDescriptor(stream, filledPullInto)를 수행한다.
-
-
-
그 밖의 경우,
-
단언: ! IsReadableStreamLocked(stream) 가 false이다.
-
! ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength)를 수행한다.
-
-
! ReadableByteStreamControllerCallPullIfNeeded(controller)를 수행한다.
-
새로운 readable byte stream queue entry를 buffer buffer, byte offset byteOffset, 그리고 byte length byteLength와 함께 controller.[[queue]]에 Append한다.
-
controller.[[queueTotalSize]]를 controller.[[queueTotalSize]] + byteLength로 설정한다.
-
cloneResult를 CloneArrayBuffer(buffer, byteOffset, byteLength,
%ArrayBuffer%
)의 값으로 둔다. -
cloneResult가 abrupt completion이면,
-
! ReadableByteStreamControllerError(controller, cloneResult.[[Value]])를 수행한다.
-
cloneResult를 반환한다.
-
-
! ReadableByteStreamControllerEnqueueChunkToQueue(controller, cloneResult.[[Value]], 0, byteLength)를 수행한다.
-
단언: pullIntoDescriptor의 reader type이 "
none
"이다. -
pullIntoDescriptor의 bytes filled > 0이면, ? ReadableByteStreamControllerEnqueueClonedChunkToQueue(controller, pullIntoDescriptor의 buffer, pullIntoDescriptor의 byte offset, pullIntoDescriptor의 bytes filled)를 수행한다.
-
! ReadableByteStreamControllerShiftPendingPullInto(controller)를 수행한다.
-
stream을 controller.[[stream]]의 값으로 둔다.
-
stream.[[state]]가 "
readable
"이 아니면, 반환한다. -
! ReadableByteStreamControllerClearPendingPullIntos(controller)를 수행한다.
-
! ResetQueue(controller)를 수행한다.
-
! ReadableByteStreamControllerClearAlgorithms(controller)를 수행한다.
-
! ReadableStreamError(stream, e)를 수행한다.
-
단언: controller.[[pendingPullIntos]] 가 비어있거나, controller.[[pendingPullIntos]][0] 이 pullIntoDescriptor이다.
-
단언: controller.[[byobRequest]]가 null이다.
-
pullIntoDescriptor의 bytes filled를 bytes filled + size로 설정한다.
-
maxBytesToCopy를 min(controller.[[queueTotalSize]], pullIntoDescriptor의 byte length − pullIntoDescriptor의 bytes filled)로 둔다.
-
maxBytesFilled를 pullIntoDescriptor의 bytes filled + maxBytesToCopy로 둔다.
-
totalBytesToCopyRemaining을 maxBytesToCopy로 둔다.
-
ready를 false로 둔다.
-
단언: ! IsDetachedBuffer(pullIntoDescriptor의 buffer)가 false이다.
-
단언: pullIntoDescriptor의 bytes filled < pullIntoDescriptor의 minimum fill이다.
-
remainderBytes를 maxBytesFilled를 pullIntoDescriptor의 element size로 나눈 나머지로 둔다.
-
maxAlignedBytes를 maxBytesFilled − remainderBytes로 둔다.
-
maxAlignedBytes ≥ pullIntoDescriptor의 minimum fill이라면,
-
totalBytesToCopyRemaining을 maxAlignedBytes − pullIntoDescriptor의 bytes filled로 설정한다.
-
ready를 true로 설정한다.
아직 최소 길이까지 채워지지 않은
read()
요청의 descriptor는 큐의 앞에 남아 있게 되므로 기저 소스가 계속 채울 수 있다.
-
-
queue를 controller.[[queue]]의 값으로 둔다.
-
반복하여 totalBytesToCopyRemaining > 0인 동안,
-
headOfQueue를 queue[0]의 값으로 둔다.
-
bytesToCopy를 min(totalBytesToCopyRemaining, headOfQueue의 byte length)로 둔다.
-
destStart를 pullIntoDescriptor의 byte offset + pullIntoDescriptor의 bytes filled로 둔다.
-
descriptorBuffer를 pullIntoDescriptor의 buffer의 값으로 둔다.
-
queueBuffer를 headOfQueue의 buffer의 값으로 둔다.
-
queueByteOffset을 headOfQueue의 byte offset의 값으로 둔다.
-
단언: ! CanCopyDataBlockBytes(descriptorBuffer, destStart, queueBuffer, queueByteOffset, bytesToCopy)가 true이다.
이 단언이 실패하면(이 명세나 구현에 버그가 있을 경우), 다음 단계에서 잠재적으로 잘못된 메모리를 읽거나 쓸 수 있다. 사용자 에이전트는 항상 이 단언을 검사하고, 실패 시 구현 정의 방식으로 중단해야 한다(예: 프로세스를 크래시시키거나, 스트림을 에러로 처리).
-
! CopyDataBlockBytes(descriptorBuffer.[[ArrayBufferData]], destStart, queueBuffer.[[ArrayBufferData]], queueByteOffset, bytesToCopy)를 수행한다.
-
headOfQueue의 byte length가 bytesToCopy와 같다면,
-
queue[0]을 제거한다.
-
-
그 밖의 경우,
-
headOfQueue의 byte offset 을 headOfQueue의 byte offset + bytesToCopy로 설정한다.
-
headOfQueue의 byte length 를 headOfQueue의 byte length − bytesToCopy로 설정한다.
-
-
controller.[[queueTotalSize]]를 controller.[[queueTotalSize]] − bytesToCopy로 설정한다.
-
! ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, bytesToCopy, pullIntoDescriptor)를 수행한다.
-
totalBytesToCopyRemaining을 totalBytesToCopyRemaining − bytesToCopy로 설정한다.
-
-
ready가 false라면,
-
단언: controller.[[queueTotalSize]]가 0이다.
-
단언: pullIntoDescriptor의 bytes filled > 0이다.
-
단언: pullIntoDescriptor의 bytes filled < pullIntoDescriptor의 minimum fill이다.
-
-
ready를 반환한다.
-
단언: controller.[[queueTotalSize]] > 0이다.
-
entry를 controller.[[queue]][0]의 값으로 둔다.
-
controller.[[queueTotalSize]]를 controller.[[queueTotalSize]] − entry의 byte length로 설정한다.
-
! ReadableByteStreamControllerHandleQueueDrain(controller)를 수행한다.
-
view를 ! Construct(
%Uint8Array%
, « entry의 buffer, entry의 byte offset, entry의 byte length »)로 둔다. -
readRequest의 chunk steps를 view를 넘겨 수행한다.
-
controller.[[byobRequest]]가 null이고 controller.[[pendingPullIntos]]가 비어있지 않다면,
-
firstDescriptor를 controller.[[pendingPullIntos]][0]의 값으로 둔다.
-
view를 ! Construct(
%Uint8Array%
, « firstDescriptor의 buffer, firstDescriptor의 byte offset + firstDescriptor의 bytes filled, firstDescriptor의 byte length − firstDescriptor의 bytes filled »)로 둔다. -
byobRequest를 new
ReadableStreamBYOBRequest
로 둔다. -
byobRequest.[[controller]]를 controller로 설정한다.
-
byobRequest.[[view]]를 view로 설정한다.
-
controller.[[byobRequest]]를 byobRequest로 설정한다.
-
-
controller.[[byobRequest]]를 반환한다.
-
state를 controller.[[stream]].[[state]]의 값으로 둔다.
-
state가 "
errored
"이면, null을 반환한다. -
state가 "
closed
"이면, 0을 반환한다. -
controller.[[strategyHWM]] − controller.[[queueTotalSize]] 값을 반환한다.
-
단언: controller.[[stream]].[[state]]가 "
readable
"이다. -
controller.[[queueTotalSize]]가 0이고 controller.[[closeRequested]]가 true라면,
-
! ReadableByteStreamControllerClearAlgorithms(controller)를 수행한다.
-
! ReadableStreamClose(controller.[[stream]])를 수행한다.
-
-
그 밖의 경우,
-
! ReadableByteStreamControllerCallPullIfNeeded(controller)를 수행한다.
-
-
controller.[[byobRequest]]가 null이면, 반환한다.
-
controller.[[byobRequest]].[[controller]] 를 undefined로 설정한다.
-
controller.[[byobRequest]].[[view]] 를 null로 설정한다.
-
controller.[[byobRequest]]를 null로 설정한다.
-
단언: controller.[[closeRequested]]가 false이다.
-
filledPullIntos를 새로운 빈 리스트로 둔다.
-
반복하여 controller.[[pendingPullIntos]]가 비어있지 않다면,
-
controller.[[queueTotalSize]]가 0이면, break한다.
-
pullIntoDescriptor를 controller.[[pendingPullIntos]][0]의 값으로 둔다.
-
! ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor)가 true라면,
-
! ReadableByteStreamControllerShiftPendingPullInto(controller)를 수행한다.
-
-
-
filledPullIntos를 반환한다.
-
reader를 controller.[[stream]].[[reader]]의 값으로 둔다.
-
단언: reader가 구현
ReadableStreamDefaultReader
를 한다. -
reader.[[readRequests]]가 비어있지 않은 동안,
-
controller.[[queueTotalSize]]가 0이면, 반환한다.
-
readRequest를 reader.[[readRequests]][0]의 값으로 둔다.
-
readRequest를 제거한다 reader.[[readRequests]]에서.
-
! ReadableByteStreamControllerFillReadRequestFromQueue(controller, readRequest)를 수행한다.
-
-
stream을 controller.[[stream]]의 값으로 둔다.
-
elementSize를 1로 둔다.
-
ctor를
%DataView%
로 둔다. -
view가 [[TypedArrayName]] 내부 슬롯을 가지고 있다면(즉,
DataView
가 아니라면),-
elementSize를 typed array 생성자 표에서 view.[[TypedArrayName]]에 대해 지정된 element size로 설정한다.
-
ctor를 typed array 생성자 표에서 view.[[TypedArrayName]]에 대해 지정된 생성자로 설정한다.
-
-
minimumFill을 min × elementSize로 둔다.
-
단언: minimumFill ≥ 0 이고 minimumFill ≤ view.[[ByteLength]]이다.
-
단언: minimumFill을 elementSize로 나눈 나머지가 0이다.
-
byteOffset을 view.[[ByteOffset]]로 둔다.
-
byteLength를 view.[[ByteLength]]로 둔다.
-
bufferResult를 TransferArrayBuffer(view.[[ViewedArrayBuffer]])의 결과로 둔다.
-
bufferResult가 abrupt completion이면,
-
readIntoRequest의 error steps를 bufferResult.[[Value]]를 넘겨 수행한다.
-
반환한다.
-
-
buffer를 bufferResult.[[Value]]로 둔다.
-
pullIntoDescriptor를 새로운 pull-into descriptor로 만드는데
- buffer
- buffer
- buffer byte length
- buffer.[[ArrayBufferByteLength]]
- byte offset
- byteOffset
- byte length
- byteLength
- bytes filled
- 0
- minimum fill
- minimumFill
- element size
- elementSize
- view constructor
- ctor
- reader type
- "
byob
"
-
controller.[[pendingPullIntos]]가 비어있지 않다면,
-
추가한다 pullIntoDescriptor를 controller.[[pendingPullIntos]]에.
-
! ReadableStreamAddReadIntoRequest(stream, readIntoRequest)를 수행한다.
-
반환한다.
-
-
stream.[[state]]가 "
closed
"라면,-
emptyView를 ! Construct(ctor, « pullIntoDescriptor의 buffer, pullIntoDescriptor의 byte offset, 0 »)로 둔다.
-
readIntoRequest의 close steps를 emptyView를 넘겨 수행한다.
-
반환한다.
-
-
controller.[[queueTotalSize]] > 0이라면,
-
! ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor)가 true라면,
-
filledView를 ! ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor)의 값으로 둔다.
-
! ReadableByteStreamControllerHandleQueueDrain(controller)를 수행한다.
-
readIntoRequest의 chunk steps를 filledView를 넘겨 수행한다.
-
반환한다.
-
-
controller.[[closeRequested]]가 true라면,
-
e를
TypeError
예외로 둔다. -
! ReadableByteStreamControllerError(controller, e)를 수행한다.
-
readIntoRequest의 error steps를 e를 넘겨 수행한다.
-
반환한다.
-
-
-
추가한다 pullIntoDescriptor를 controller.[[pendingPullIntos]]에.
-
! ReadableStreamAddReadIntoRequest(stream, readIntoRequest)를 수행한다.
-
! ReadableByteStreamControllerCallPullIfNeeded(controller)를 수행한다.
-
단언: controller.[[pendingPullIntos]]가 비어있지 않다.
-
firstDescriptor를 controller.[[pendingPullIntos]][0]의 값으로 둔다.
-
state를 controller.[[stream]].[[state]]의 값으로 둔다.
-
state가 "
closed
"라면,-
bytesWritten이 0이 아니라면,
TypeError
예외를 throw 한다.
-
-
그 밖의 경우,
-
단언: state가 "
readable
"이다. -
bytesWritten이 0이라면,
TypeError
예외를 throw 한다. -
firstDescriptor의 bytes filled + bytesWritten > firstDescriptor의 byte length라면,
RangeError
예외를 throw 한다.
-
-
firstDescriptor의 buffer를 ! TransferArrayBuffer(firstDescriptor의 buffer)로 설정한다.
-
? ReadableByteStreamControllerRespondInternal(controller, bytesWritten)를 수행한다.
-
단언: firstDescriptor의 bytes filled를 firstDescriptor의 element size로 나눈 나머지가 0이다.
-
firstDescriptor의 reader type이 "
none
"이면, ! ReadableByteStreamControllerShiftPendingPullInto(controller)를 수행한다. -
stream을 controller.[[stream]]의 값으로 둔다.
-
! ReadableStreamHasBYOBReader(stream) 가 true라면,
-
filledPullIntos를 새로운 빈 리스트로 둔다.
-
반복하여 filledPullIntos의 size가 ! ReadableStreamGetNumReadIntoRequests(stream)보다 작을 때,
-
pullIntoDescriptor를 ! ReadableByteStreamControllerShiftPendingPullInto(controller)의 값으로 둔다.
-
-
각 filledPullInto를 filledPullIntos에서,
-
! ReadableByteStreamControllerCommitPullIntoDescriptor(stream, filledPullInto)를 수행한다.
-
-
-
단언: pullIntoDescriptor의 bytes filled + bytesWritten ≤ pullIntoDescriptor의 byte length이다.
-
! ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, bytesWritten, pullIntoDescriptor)를 수행한다.
-
pullIntoDescriptor의 reader type이 "
none
"이면,-
? ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(controller, pullIntoDescriptor)를 수행한다.
-
filledPullIntos를 ! ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller)의 결과로 둔다.
-
각 filledPullInto를 filledPullIntos에서,
-
! ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]], filledPullInto)를 수행한다.
-
-
반환한다.
-
-
pullIntoDescriptor의 bytes filled < pullIntoDescriptor의 minimum fill이면, 반환한다.
아직 최소 길이까지 채워지지 않은
read()
요청의 descriptor는 큐의 앞에 남아 있게 되므로 기저 소스가 계속 채울 수 있다. -
! ReadableByteStreamControllerShiftPendingPullInto(controller)를 수행한다.
-
remainderSize를 pullIntoDescriptor의 bytes filled를 pullIntoDescriptor의 element size로 나눈 나머지로 둔다.
-
remainderSize > 0이면,
-
end을 pullIntoDescriptor의 byte offset + pullIntoDescriptor의 bytes filled로 둔다.
-
? ReadableByteStreamControllerEnqueueClonedChunkToQueue(controller, pullIntoDescriptor의 buffer, end − remainderSize, remainderSize)를 수행한다.
-
-
pullIntoDescriptor의 bytes filled를 pullIntoDescriptor의 bytes filled − remainderSize로 설정한다.
-
filledPullIntos를 ! ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller)의 결과로 둔다.
-
! ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]], pullIntoDescriptor).
-
각 filledPullInto를 filledPullIntos에서,
-
! ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]], filledPullInto)를 수행한다.
-
-
firstDescriptor를 controller.[[pendingPullIntos]][0]의 값으로 둔다.
-
단언: ! CanTransferArrayBuffer(firstDescriptor의 buffer)가 true이다.
-
! ReadableByteStreamControllerInvalidateBYOBRequest(controller)를 수행한다.
-
state를 controller.[[stream]].[[state]]의 값으로 둔다.
-
state가 "
closed
"이면,-
단언: bytesWritten이 0이다.
-
! ReadableByteStreamControllerRespondInClosedState(controller, firstDescriptor)를 수행한다.
-
-
그 밖의 경우,
-
단언: state가 "
readable
"이다. -
단언: bytesWritten > 0이다.
-
? ReadableByteStreamControllerRespondInReadableState(controller, bytesWritten, firstDescriptor)를 수행한다.
-
-
! ReadableByteStreamControllerCallPullIfNeeded(controller)를 수행한다.
-
단언: controller.[[pendingPullIntos]]가 비어있지 않다.
-
단언: ! IsDetachedBuffer(view.[[ViewedArrayBuffer]]) 가 false이다.
-
firstDescriptor를 controller.[[pendingPullIntos]][0]의 값으로 둔다.
-
state를 controller.[[stream]].[[state]]의 값으로 둔다.
-
state가 "
closed
"이면,-
view.[[ByteLength]]가 0이 아니면,
TypeError
예외를 throw 한다.
-
-
그 밖의 경우,
-
단언: state가 "
readable
"이다. -
view.[[ByteLength]]가 0이면,
TypeError
예외를 throw 한다.
-
-
firstDescriptor의 byte offset + firstDescriptor의 bytes filled가 view.[[ByteOffset]]와 다르면,
RangeError
예외를 throw 한다. -
firstDescriptor의 buffer byte length가 view.[[ViewedArrayBuffer]].[[ByteLength]]와 다르면,
RangeError
예외를 throw 한다. -
firstDescriptor의 bytes filled + view.[[ByteLength]] > firstDescriptor의 byte length이면,
RangeError
예외를 throw 한다. -
viewByteLength를 view.[[ByteLength]]의 값으로 둔다.
-
firstDescriptor의 buffer를 ? TransferArrayBuffer(view.[[ViewedArrayBuffer]])로 설정한다.
-
? ReadableByteStreamControllerRespondInternal(controller, viewByteLength)를 수행한다.
-
단언: controller.[[byobRequest]]가 null이다.
-
descriptor를 controller.[[pendingPullIntos]][0]의 값으로 둔다.
-
descriptor를 제거한다 controller.[[pendingPullIntos]]에서.
-
descriptor를 반환한다.
-
stream을 controller.[[stream]]의 값으로 둔다.
-
stream.[[state]]가 "
readable
"이 아니면, false를 반환한다. -
controller.[[closeRequested]]가 true이면, false를 반환한다.
-
controller.[[started]]가 false이면, false를 반환한다.
-
! ReadableStreamHasDefaultReader(stream) 가 true이고 ! ReadableStreamGetNumReadRequests(stream) > 0이면, true를 반환한다.
-
! ReadableStreamHasBYOBReader(stream) 가 true이고 ! ReadableStreamGetNumReadIntoRequests(stream) > 0이면, true를 반환한다.
-
desiredSize를 ! ReadableByteStreamControllerGetDesiredSize(controller)의 값으로 둔다.
-
단언: desiredSize가 null이 아니다.
-
desiredSize > 0이면, true를 반환한다.
-
false를 반환한다.
-
단언: stream.[[controller]]가 undefined이다.
-
autoAllocateChunkSize가 undefined가 아니라면,
-
단언: ! IsInteger(autoAllocateChunkSize)가 true이다.
-
단언: autoAllocateChunkSize가 양수이다.
-
-
controller.[[stream]]를 stream으로 설정한다.
-
controller.[[pullAgain]]과 controller.[[pulling]]을 false로 설정한다.
-
controller.[[byobRequest]]를 null로 설정한다.
-
! ResetQueue(controller)를 수행한다.
-
controller.[[closeRequested]]와 controller.[[started]]를 false로 설정한다.
-
controller.[[strategyHWM]]을 highWaterMark로 설정한다.
-
controller.[[pullAlgorithm]]을 pullAlgorithm으로 설정한다.
-
controller.[[cancelAlgorithm]]을 cancelAlgorithm으로 설정한다.
-
controller.[[autoAllocateChunkSize]] 를 autoAllocateChunkSize로 설정한다.
-
controller.[[pendingPullIntos]]를 새로운 빈 리스트로 설정한다.
-
stream.[[controller]]를 controller로 설정한다.
-
startResult를 startAlgorithm을 수행한 결과로 둔다.
-
startPromise를 startResult로 resolve된 promise로 둔다.
-
-
controller.[[started]]를 true로 설정한다.
-
단언: controller.[[pulling]]이 false이다.
-
단언: controller.[[pullAgain]]이 false이다.
-
! ReadableByteStreamControllerCallPullIfNeeded(controller)를 수행한다.
-
-
startPromise가 거부될 때, 이유 r와 함께,
-
! ReadableByteStreamControllerError(controller, r)를 수행한다.
-
-
controller를 new
ReadableByteStreamController
로 둔다. -
startAlgorithm을 undefined를 반환하는 알고리즘으로 둔다.
-
pullAlgorithm을 undefined로 resolve된 promise를 반환하는 알고리즘으로 둔다.
-
cancelAlgorithm을 undefined로 resolve된 promise를 반환하는 알고리즘으로 둔다.
-
underlyingSourceDict["
start
"] 가 존재하면, startAlgorithm을 invoke하여, underlyingSourceDict["start
"] 에 인자 목록 « controller » 및 callback this value underlyingSource를 전달하는 알고리즘으로 설정한다. -
underlyingSourceDict["
pull
"] 가 존재하면, pullAlgorithm을 invoke하여, underlyingSourceDict["pull
"] 에 인자 목록 « controller » 및 callback this value underlyingSource를 전달하는 알고리즘으로 설정한다. -
underlyingSourceDict["
cancel
"] 가 존재하면, cancelAlgorithm을 인자 reason을 받아, invoke하여 underlyingSourceDict["cancel
"] 에 인자 목록 « reason » 및 callback this value underlyingSource를 전달하는 알고리즘으로 설정한다. -
autoAllocateChunkSize를 underlyingSourceDict["
autoAllocateChunkSize
"]가 존재하면 그 값, 아니면 undefined로 둔다. -
autoAllocateChunkSize가 0이면,
TypeError
예외를 throw 한다. -
? SetUpReadableByteStreamController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, autoAllocateChunkSize)를 수행한다.
5. Writable streams
5.1. Writable 스트림 사용하기
readableStream. pipeTo( writableStream) . then(() => console. log( "모든 데이터가 성공적으로 기록되었습니다!" )) . catch ( e=> console. error( "문제가 발생했습니다!" , e));
write()
및 close()
메서드를 사용합니다. writable 스트림은 들어오는 모든 write를 큐에 저장하고, 내부적으로 underlying sink으로 순차적으로 전달하므로,
복잡하게 신경 쓸 필요 없이 데이터를 쓸 수 있습니다.
function writeArrayToStream( array, writableStream) { const writer= writableStream. getWriter(); array. forEach( chunk=> writer. write( chunk). catch (() => {})); return writer. close(); } writeArrayToStream([ 1 , 2 , 3 , 4 , 5 ], writableStream) . then(() => console. log( "모두 완료되었습니다!" )) . catch ( e=> console. error( "스트림 오류: " + e));
.catch(() => {})
패턴을 사용하여 write()
메서드의 reject를 억제합니다. 치명적인 오류는 close()
메서드의 reject로 알림을 받게 되며, 이를 처리하지 않으면
unhandledrejection
이벤트 및 콘솔 경고가 발생할 수 있습니다.
close()
메서드가 반환하는 promise만을 보고 전체 스트림의 성공 또는 실패만을 신경썼습니다.
이 promise는 스트림의 초기화, writing, closing 중 어떤 문제가 발생해도 reject되고, 스트림이 성공적으로 닫히면 fulfill 됩니다. 대부분의 경우 이 정도만 확인하면
충분합니다.
하지만 특정 chunk가 성공적으로 기록되었는지 신경 쓴다면,
writer의 write()
메서드가 반환하는 promise를 사용할 수 있습니다:
writer. write( "i am a chunk of data" ) . then(() => console. log( "청크가 성공적으로 기록되었습니다!" )) . catch ( e=> console. error( e));
여기서 "성공"의 의미는 각 스트림 인스턴스(정확히는 underlying sink)에 따라 다릅니다. 예를 들어 파일 스트림의 경우 운영체제가 write를 받아들인 것만 의미할 수 있으며, 반드시 디스크에 기록되었다는 뜻은 아닙니다. 어떤 스트림은 이러한 신호를 전혀 제공하지 못할 수도 있고, 이 경우 반환된 promise는 즉시 fulfill됩니다.
desiredSize
와 ready
속성을 사용하면 writable stream writer에서
producer가 스트림의 흐름 제어 신호에 더 정밀하게 반응하여
스트림의 high water mark 이하로
메모리 사용량을 유지할 수 있습니다.
다음 예제에서는 무한히 랜덤 바이트를 스트림에 기록하며, desiredSize
값만큼 바이트를 만들고 ready
를 활용해 backpressure가 해소될 때까지
대기합니다.
async function writeRandomBytesForever( writableStream) { const writer= writableStream. getWriter(); while ( true ) { await writer. ready; const bytes= new Uint8Array( writer. desiredSize); crypto. getRandomValues( bytes); // 특별히 await하지 않습니다; writer.ready를 await하는 것으로 충분합니다. writer. write( bytes). catch (() => {}); } } writeRandomBytesForever( myWritableStream). catch ( e=> console. error( "문제가 발생했습니다" , e));
여기서는 write()
의 반환 promise를 await
하지 않습니다. writer.ready
의 promise만
await
하기 때문입니다.
또한 이전 예제처럼 .catch(() =>
{})
패턴을 사용합니다.
이 경우 writer.ready
promise를 await
하는 과정에서 실패 알림을 받게 됩니다.
write()
가 반환하는 promise를 await
하는 것이 좋지 않다는 점을 강조하기 위해,
위 예제의 변형을 생각해봅시다. 여기서는 WritableStreamDefaultWriter
인터페이스를 직접 사용하지만, 한 번에 써야 할 바이트 수를 제어하지 않습니다. 이 경우 backpressure를 처리하는 코드는 동일합니다:
async function writeSuppliedBytesForever( writableStream, getBytes) { const writer= writableStream. getWriter(); while ( true ) { await writer. ready; const bytes= getBytes(); writer. write( bytes). catch (() => {}); } }
이전 예제에서는 항상 writer.desiredSize
만큼만 썼기 때문에,
write()
와 ready
promise가 동기화되어 있었습니다. 하지만 여기서는 ready
promise가 write()
promise보다 먼저 fulfill될 수도 있습니다.
ready
promise는 desired size가 양수가 될 때 fulfill되기
때문인데,
이는 write가 성공하기 전일 수도 있습니다 (특히 high water mark가 큰 경우).
즉, write()
의 반환 값을 await
하면 스트림의 internal queue에 write를 큐잉하지 않고,
이전 write가 성공한 후에만 다음 write를 실행하게 되어 처리량이 낮아질 수 있습니다.
5.2. WritableStream
클래스
WritableStream
은 writable stream을 나타냅니다.
5.2.1. 인터페이스 정의
WritableStream
클래스의 Web IDL 정의는 다음과 같습니다:
[Exposed=*,Transferable ]interface {
WritableStream constructor (optional object ,
underlyingSink optional QueuingStrategy = {});
strategy readonly attribute boolean locked ;Promise <undefined >abort (optional any );
reason Promise <undefined >close ();WritableStreamDefaultWriter getWriter (); };
5.2.2. 내부 슬롯
WritableStream
인스턴스는 다음 표에 설명된 내부 슬롯들과 함께 생성됩니다:
내부 슬롯 | 설명 (비규범적) |
---|---|
[[backpressure]] | 컨트롤러가 설정한 backpressure 신호를 나타내는 불리언 값 |
[[closeRequest]] | writer의 close()
메서드가 반환한 promise
|
[[controller]] | WritableStreamDefaultController
로, 이 스트림의 상태와 큐를 제어할 수 있도록 생성됨
|
[[Detached]] | 스트림이 전달(transferred)될 때 true로 설정되는 불리언 플래그 |
[[inFlightWriteRequest]] | underlying sink의 write 알고리즘이 실행 중이고 아직 완료되지 않았을 때, 현재 진행 중인 write 작업에 대한 promise를 저장하는 슬롯. 재진입 호출을 방지하기 위해 사용됨 |
[[inFlightCloseRequest]] | underlying sink의 close 알고리즘이 실행 중이고 아직 완료되지 않았을 때, 현재 진행
중인 close 작업에 대한 promise를 저장하는 슬롯. abort()
메서드가 close를 방해하지 않도록 함
|
[[pendingAbortRequest]] | pending abort request |
[[state]] | 스트림의 현재 상태를 나타내는 문자열로, 내부적으로 사용됨. 값은 "writable ",
"closed ", "erroring ", "errored " 중 하나
|
[[storedError]] | 스트림이 실패한 이유를 나타내는 값으로, 스트림이 "errored " 상태일 때 동작을 시도하면 예외 또는 실패
이유로 사용됨
|
[[writer]] | 스트림이 writer에
lock되어 있으면 WritableStreamDefaultWriter
인스턴스, lock되어 있지 않으면 undefined
|
[[writeRequests]] | list로, 아직 underlying sink에서 처리되지 않은 내부 write 요청의 promise들을 나타냄 |
[[inFlightCloseRequest]] 슬롯과 [[closeRequest]] 슬롯은 상호 배타적입니다. 또한 [[inFlightWriteRequest]] 가 undefined가 아닐 때는 [[writeRequests]]에서 요소가 제거되지 않습니다. 구현체는 이러한 불변식을 기반으로 슬롯의 저장을 최적화할 수 있습니다.
pending abort request는 스트림을 abort하려는 요청을 최종적으로 처리하기 전에 추적하기 위한 struct입니다. 다음 item들을 가집니다:
- promise
-
WritableStreamAbort에서 반환된 promise
- reason
-
WritableStreamAbort에 abort 이유로 전달된 JavaScript 값
- was already erroring
-
WritableStreamAbort가 호출될 때 스트림이 "
erroring
" 상태였는지 여부를 나타내는 불리언 값으로, abort 요청의 결과에 영향을 줍니다
5.2.3. underlying sink API
WritableStream()
생성자는 첫 번째 인자로 underlying
sink을 나타내는 JavaScript 객체를 받습니다.
이러한 객체는 다음 속성 중 하나 이상을 포함할 수 있습니다:
dictionary {
UnderlyingSink UnderlyingSinkStartCallback start ;UnderlyingSinkWriteCallback write ;UnderlyingSinkCloseCallback close ;UnderlyingSinkAbortCallback abort ;any type ; };callback =
UnderlyingSinkStartCallback any (WritableStreamDefaultController );
controller callback =
UnderlyingSinkWriteCallback Promise <undefined > (any ,
chunk WritableStreamDefaultController );
controller callback =
UnderlyingSinkCloseCallback Promise <undefined > ();callback =
UnderlyingSinkAbortCallback Promise <undefined > (optional any );
reason
start(controller)
, 타입: UnderlyingSinkStartCallback-
WritableStream
이 생성될 때 즉시 호출되는 함수입니다.일반적으로 이것은 underlying sink 리소스에 접근하기 위해 사용됩니다.
이 설정 과정이 비동기라면 성공 또는 실패를 알리기 위해 promise를 반환할 수 있습니다. promise가 reject되면 스트림에 에러가 발생합니다. 예외가 발생하면
WritableStream()
생성자에서 다시 throw됩니다. write(chunk, controller)
, 타입: UnderlyingSinkWriteCallback-
새로운 chunk 데이터가 underlying sink에 기록될 준비가 되면 호출됩니다. 스트림 구현은 이전 write가 성공한 후에만 이 함수가 호출되도록 보장하며,
start()
가 성공하기 전에나close()
또는abort()
가 호출된 이후에는 절대 호출되지 않습니다.이 함수는 실제로 underlying sink가 표현하는 리소스에 데이터를 전달하는 데 사용됩니다. 예를 들어, 하위 레벨 API를 호출할 수 있습니다.
데이터 기록 과정이 비동기이고 성공/실패 신호를 사용자에게 전달할 수 있다면, 이 함수는 promise를 반환하여 성공 또는 실패를 알릴 수 있습니다. 이 promise의 반환값은
writer.write()
를 호출한 쪽에 전달되어 개별 write 상태를 모니터링할 수 있습니다. 예외가 발생하면 reject된 promise를 반환한 것과 동일하게 처리됩니다.이러한 신호가 항상 제공되는 것은 아니므로, § 10.6 백프레셔나 성공 신호가 없는 writable stream과 § 10.7 백프레셔와 성공 신호가 있는 writable stream을 비교해 보세요. 그런 경우에는 아무 것도 반환하지 않는 것이 가장 좋습니다.
이 함수에서 반환될 수 있는 promise는 또한 스트림 내부 큐의 desired size 계산에 해당 chunk가 기록된 것으로 간주되는지 여부를 결정합니다. 즉, promise가 settle되는 동안
writer.desiredSize
값은 이전 상태를 유지하며, write가 완료되어야만 새로운 chunk의 필요가 신호됩니다.마지막으로, 이 promise는 적절하게 동작하는 producer가 chunk가 완전히 처리되기 전에 chunk를 변경하려 하지 않도록 보장하는 데에도 사용됩니다. (이는 어떤 명세에서도 강제하지 않으며, producer와 underlying sink 사이의 비공식적 계약입니다.)
close()
, 타입: UnderlyingSinkCloseCallback-
producer가
writer.close()
를 통해 더 이상 스트림에 chunk를 기록하지 않음을 알리고, 큐에 있던 모든 write가 성공적으로 완료된 후 호출되는 함수입니다.이 함수는 underlying sink에 데이터를 최종적으로 기록하거나 flush하는 작업, 자원 해제 등을 수행할 수 있습니다.
종료 과정이 비동기라면 이 함수는 성공/실패를 알릴 수 있도록 promise를 반환할 수 있습니다. 결과는
writer.close()
메서드의 반환값을 통해 전달됩니다. promise가 reject되면 스트림은 정상적으로 닫히지 않고 에러가 발생합니다. 예외가 발생하면 reject된 promise와 동일하게 처리됩니다. abort(reason)
, 타입: UnderlyingSinkAbortCallback-
producer가
stream.abort()
또는writer.abort()
를 통해 스트림을 강제 종료(abort)하고자 할 때 호출되는 함수입니다. 인자는 producer가 해당 메서드에 전달한 값과 동일합니다.Writable stream은 파이핑(piping) 중에도 특정 조건에서 abort될 수 있습니다. 자세한 내용은
pipeTo()
메서드 정의를 참고하세요.이 함수는
close()
처럼 자원을 정리할 수 있지만, 별도의 처리를 할 수도 있습니다.종료 과정이 비동기라면 promise를 반환하여 성공/실패를 알릴 수 있으며, 결과는
writer.abort()
메서드의 반환값을 통해 전달됩니다. 예외가 발생하면 reject된 promise와 동일하게 처리됩니다. 어떠한 경우든 스트림은 abort되었다는 새로운TypeError
로 에러 상태가 됩니다. type
, 타입: any-
이 속성은 향후 사용을 위해 예약되어 있으므로 값을 제공하려고 하면 예외가 발생합니다.
start()
및
write()
에 전달되는 controller
인자는 WritableStreamDefaultController
인스턴스이며, 스트림에 에러를 발생시킬 수 있습니다. 이는 promise 기반이 아닌 API와의 연계를 위해 주로 사용됩니다. 예시는 § 10.6 백프레셔나 성공 신호가 없는 writable stream에서 볼 수 있습니다.
5.2.4. 생성자, 메서드, 프로퍼티
stream = new
WritableStream
(underlyingSink[, strategy)-
제공된 underlying sink을 감싸는 새로운
WritableStream
을 생성합니다. underlyingSink 인자에 대한 자세한 내용은 § 5.2.3 underlying sink API를 참고하세요.strategy 인자는 스트림의 큐잉 전략을 나타내며, § 7.1 큐잉 전략 API에서 설명되어 있습니다. 제공되지 않으면 기본 동작은
CountQueuingStrategy
와 high water mark가 1인 것과 동일합니다. isLocked = stream.
locked
-
writable stream이 writer에 lock되어 있는지 여부를 반환합니다.
await stream.
abort
([ reason ])-
스트림을 강제 종료(abort)하여 producer가 더 이상 성공적으로 쓰기를 할 수 없음을 알리고, 스트림을 즉시 에러 상태로 이동시키며, 큐에 있던 쓰기는 모두 버립니다. 이는 underlying sink의 abort 메커니즘도 실행됩니다.
반환된 promise는 스트림이 성공적으로 종료되면 fulfill되고, underlying sink에서 에러가 발생하면 reject됩니다. 또한, 스트림이 현재 lock되어 있으면
TypeError
로 reject됩니다(스트림을 취소하려 시도하지 않습니다). await stream.
close
()-
스트림을 닫습니다. underlying sink은 이전에 기록된 chunk들을 모두 처리한 후 닫기 동작을 수행합니다. 이 동안 추가적인 write 시도는 실패하지만(스트림에 에러가 발생하지는 않음) 처리되지 않습니다.
이 메서드는 남아있는 chunk들이 모두 성공적으로 기록되고 스트림이 정상적으로 닫히면 fulfill되는 promise를 반환합니다. 이 과정에서 에러가 발생하면 reject됩니다. 또한, 스트림이 현재 lock되어 있으면
TypeError
로 reject됩니다(스트림을 취소하려 시도하지 않습니다). writer = stream.
getWriter
()-
writer (
WritableStreamDefaultWriter
인스턴스)를 생성하고, 스트림을 새 writer에 lock합니다. 스트림이 lock된 동안에는 다른 writer를 얻을 수 없으며, 기존 writer가 release되어야만 가능합니다.이 기능은 스트림에 끼어들기나 중단 없이 쓸 수 있는 추상화를 만들 때 특히 유용합니다. 스트림에 writer를 얻으면, 동시에 다른 사람이 쓸 수 없으므로 기록된 데이터가 예측 불가하거나 쓸모없게 되는 상황을 방지할 수 있습니다.
new WritableStream(underlyingSink, strategy)
생성자 단계:
-
underlyingSink가 없는 경우, null로 설정한다.
-
underlyingSinkDict를 underlyingSink의 IDL 값으로 변환한 값으로 설정한다. 타입은
UnderlyingSink
이다.인자를
UnderlyingSink
타입으로 직접 선언할 수 없는 이유는 원래 객체에 대한 참조를 유지해야 하기 때문입니다. 원래 객체를 유지해야 다양한 메서드를 호출할 수 있습니다. -
underlyingSinkDict["
type
"] 가 존재하면,RangeError
예외를 throw한다.이는 향후 새로운 타입을 추가하기 위해, 하위 호환성 문제를 방지하기 위한 것입니다.
-
! InitializeWritableStream(this)를 수행한다.
-
sizeAlgorithm을 ! ExtractSizeAlgorithm(strategy)로 설정한다.
-
highWaterMark를 ? ExtractHighWaterMark(strategy, 1)로 설정한다.
-
? SetUpWritableStreamDefaultControllerFromUnderlyingSink(this, underlyingSink, underlyingSinkDict, highWaterMark, sizeAlgorithm)를 수행한다.
locked
getter 단계:
-
! IsWritableStreamLocked(this)를 반환한다.
abort(reason)
메서드 단계:
-
! IsWritableStreamLocked(this)가 true이면, TypeError 예외로 reject된 promise를 반환한다.
-
! WritableStreamAbort(this, reason)를 반환한다.
close()
메서드 단계:
-
! IsWritableStreamLocked(this)가 true이면, TypeError 예외로 reject된 promise를 반환한다.
-
! WritableStreamCloseQueuedOrInFlight(this)가 true이면, TypeError 예외로 reject된 promise를 반환한다.
-
! WritableStreamClose(this)를 반환한다.
getWriter()
메서드 단계:
-
? AcquireWritableStreamDefaultWriter(this)를 반환한다.
5.2.5. postMessage()
를 통한 전송
destination.postMessage(ws, { transfer: [ws] });
-
WritableStream
을 다른 프레임, 윈도우, 또는 워커로 전송합니다.전송된 스트림은 원래와 동일하게 사용할 수 있습니다. 원래 스트림은 lock되어 더 이상 직접 사용할 수 없습니다.
WritableStream
객체는 전송 가능한 객체입니다. value와 dataHolder가 주어졌을 때의 전송 단계는 다음과 같습니다:
-
! IsWritableStreamLocked(value)가 true이면, "
DataCloneError
"DOMException
을 throw한다. -
port1을 새로운
MessagePort
(현재 Realm에서)로 설정한다. -
port2을 새로운
MessagePort
(현재 Realm에서)로 설정한다. -
Entangle port1과 port2를 연결한다.
-
readable을 새로운
ReadableStream
(현재 Realm에서)로 설정한다. -
! SetUpCrossRealmTransformReadable(readable, port1)를 실행한다.
-
promise를 ! ReadableStreamPipeTo(readable, value, false, false, false)로 설정한다.
-
promise.[[PromiseIsHandled]]를 true로 설정한다.
-
dataHolder.[[port]]를 ! StructuredSerializeWithTransfer(port2, « port2 »)로 설정한다.
-
deserializedRecord를 ! StructuredDeserializeWithTransfer(dataHolder.[[port]], 현재 Realm)로 설정한다.
-
port를 deserializedRecord.[[Deserialized]]로 설정한다.
-
! SetUpCrossRealmTransformWritable(value, port)를 수행한다.
5.3. WritableStreamDefaultWriter
클래스
WritableStreamDefaultWriter
클래스는 writable stream writer를 나타내며,
WritableStream
인스턴스에서 생성되도록 설계되었습니다.
5.3.1. 인터페이스 정의
WritableStreamDefaultWriter
클래스의 Web IDL 정의는 다음과 같습니다:
[Exposed=*]interface {
WritableStreamDefaultWriter constructor (WritableStream );
stream readonly attribute Promise <undefined >closed ;readonly attribute unrestricted double ?desiredSize ;readonly attribute Promise <undefined >ready ;Promise <undefined >abort (optional any );
reason Promise <undefined >close ();undefined releaseLock ();Promise <undefined >write (optional any ); };
chunk
5.3.2. 내부 슬롯
WritableStreamDefaultWriter
인스턴스는 아래 표에 설명된 내부 슬롯들과 함께 생성됩니다:
내부 슬롯 | 설명 (비규범적) |
---|---|
[[closedPromise]] | writer의
closed
getter가 반환하는 promise
|
[[readyPromise]] | writer의
ready
getter가 반환하는 promise
|
[[stream]] | 이 reader를 소유하는 WritableStream
인스턴스
|
5.3.3. 생성자, 메서드, 프로퍼티
writer = new
WritableStreamDefaultWriter
(stream)-
이는
stream.
를 호출하는 것과 동일합니다.getWriter()
await writer.
closed
-
스트림이 닫히면 fulfill되고, 스트림에서 에러가 발생하거나 writer의 lock이 스트림이 닫히기 전에 release되면 reject되는 promise를 반환합니다.
desiredSize = writer.
desiredSize
-
스트림 내부 큐를 채우기 위한 desired size를 반환합니다. 큐가 초과되면 음수가 될 수 있습니다. producer는 이 정보를 이용해 적절한 쓰기 양을 결정할 수 있습니다.
스트림이 에러 상태이거나 abort가 큐잉된 경우에는 null을 반환합니다. 스트림이 닫혔을 때는 0을 반환합니다. 그리고 writer의 lock이 release된 상태에서 호출하면 예외가 발생합니다.
await writer.
ready
-
스트림 내부 큐의 desired size가 0 이하에서 양수로 바뀔 때 fulfill되는 promise를 반환합니다. 이는 backpressure가 해제되었음을 의미합니다. desired size가 다시 0 이하로 내려가면, getter는 다음 변환까지 pending 상태인 새 promise를 반환합니다.
스트림이 에러 상태나 abort 상태가 되거나 writer의 lock이 release되면 반환된 promise는 reject됩니다.
await writer.
abort
([ reason ])await writer.
close
()writer.
releaseLock
()-
해당 스트림에 대한 writer의 lock을 해제합니다. lock이 해제되면 writer는 더 이상 active 상태가 아닙니다. 스트림이 lock 해제 시 에러 상태이면, writer도 동일하게 에러 상태로 간주되고, 그렇지 않으면 writer는 닫힌 것으로 간주됩니다.
lock은 아직 완료되지 않은 write가 있더라도 해제할 수 있습니다(즉, 이전
write()
호출에서 반환된 promise가 아직 settle되지 않았더라도). writer의 lock은 전체 write 동안 유지할 필요가 없으며, lock이 있으면 다른 producer가 중첩하여 쓰기를 시도하는 것을 방지합니다. await writer.
write
(chunk)-
주어진 chunk를 writable stream에 기록합니다. 이전 쓰기가 성공적으로 완료될 때까지 기다린 후, underlying sink의
write()
메서드에 chunk를 전달합니다. 성공적으로 기록되면 undefined로 fulfill되는 promise를, 쓰기 실패 또는 스트림이 에러 상태로 변경되면 reject되는 promise를 반환합니다.여기서 "성공"의 의미는 underlying sink에 따라 다릅니다. 단순히 chunk가 받아들여졌다는 의미일 수도 있고, 반드시 최종 목적지에 안전하게 저장된 것은 아닐 수 있습니다.
chunk가 변경 가능한(mutable) 값인 경우, producer는
write()
호출 후 반환된 promise가 settle되기 전까지는 chunk를 변경하지 않는 것이 좋습니다. 이렇게 해야 underlying sink가 전달받은 값 그대로 처리할 수 있습니다.
new WritableStreamDefaultWriter(stream)
생성자 단계:
-
? SetUpWritableStreamDefaultWriter(this, stream)를 수행한다.
closed
getter 단계:
-
this.[[closedPromise]]를 반환한다.
desiredSize
getter 단계:
-
this.[[stream]]이 undefined라면,
TypeError
예외를 throw한다.
ready
getter
단계:
-
this.[[readyPromise]]를 반환한다.
abort(reason)
메서드 단계:
-
this.[[stream]]이 undefined라면, TypeError 예외로 reject된 promise를 반환한다.
-
! WritableStreamDefaultWriterAbort(this, reason)를 반환한다.
close()
메서드
단계:
-
stream을 this.[[stream]]으로 한다.
-
stream이 undefined라면, TypeError 예외로 reject된 promise를 반환한다.
-
! WritableStreamCloseQueuedOrInFlight(stream) 가 true이면, TypeError 예외로 reject된 promise를 반환한다.
-
! WritableStreamDefaultWriterClose(this)를 반환한다.
releaseLock()
메서드 단계:
-
stream을 this.[[stream]]으로 한다.
-
stream이 undefined라면, 반환한다.
-
단언: stream.[[writer]]는 undefined가 아니다.
-
! WritableStreamDefaultWriterRelease(this)를 수행한다.
write(chunk)
메서드 단계:
-
this.[[stream]]이 undefined라면, TypeError 예외로 reject된 promise를 반환한다.
-
! WritableStreamDefaultWriterWrite(this, chunk)를 반환한다.
5.4.
WritableStreamDefaultController
클래스
WritableStreamDefaultController
클래스에는 WritableStream
의 상태를 제어할 수 있는 메서드가 있습니다. WritableStream
을 생성할 때, underlying
sink에게 해당 WritableStreamDefaultController
인스턴스를 전달하여 조작할 수 있게 합니다.
5.4.1. 인터페이스 정의
WritableStreamDefaultController
클래스의 Web IDL 정의는 다음과 같습니다:
[Exposed=*]interface {
WritableStreamDefaultController readonly attribute AbortSignal signal ;undefined error (optional any ); };
e
5.4.2. 내부 슬롯
WritableStreamDefaultController
인스턴스는 아래 표에 설명된 내부 슬롯들과 함께 생성됩니다:
내부 슬롯 | 설명 (비규범적) |
---|---|
[[abortAlgorithm]] | 하나의 인자(중단 이유)를 받아 underlying sink에 abort 요청을 전달하는, promise를 반환하는 알고리즘 |
[[abortController]] | 스트림이 중단(aborted)될 때, 대기 중인 쓰기 또는 닫기 작업을 중단할 수 있는 AbortController
인스턴스
|
[[closeAlgorithm]] | underlying sink에 close 요청을 전달하는, promise를 반환하는 알고리즘 |
[[queue]] | list로, 스트림의 내부 chunk 큐를 나타냅니다 |
[[queueTotalSize]] | [[queue]]에 저장된 모든 chunk의 전체 크기 (§ 8.1 크기가 있는 큐 참고) |
[[started]] | underlying sink의 초기화가 끝났는지 여부를 나타내는 불리언 플래그 |
[[strategyHWM]] | 스트림 생성 시 큐잉 전략(queuing strategy)의 일부로 지정된 값으로, 이 값에 도달하면 backpressure가 underlying sink에 적용됨 |
[[strategySizeAlgorithm]] | 스트림의 큐잉 전략의 일부로, 큐에 추가되는 chunk의 크기를 계산하는 알고리즘 |
[[stream]] | 제어되는 WritableStream
인스턴스
|
[[writeAlgorithm]] | 하나의 인자(쓰기할 chunk)를 받아 underlying sink에 데이터를 쓰는, promise를 반환하는 알고리즘 |
close sentinel은 [[queue]]에 chunk 대신 삽입되어 스트림이 닫혔음을 알리는 고유한 값입니다. 내부적으로만 사용되며, 웹 개발자에게 노출되지 않습니다.
5.4.3. 메서드 및 프로퍼티
controller.
signal
-
스트림이 중단(aborted)될 때, 대기 중인 쓰기/닫기 작업을 중단할 수 있는 AbortSignal입니다.
controller.
error
(e)-
제어 중인 writable stream을 닫고, 앞으로의 모든 상호작용이 주어진 에러 e로 실패하도록 만듭니다.
이 메서드는 드물게 사용되며, 일반적으로는 underlying sink의 메서드 중 하나에서 reject된 promise를 반환하는 것으로 충분합니다. 다만, underlying sink와의 일반적인 생명주기 외의 이벤트에 반응하여 스트림을 갑자기 종료해야 할 때 유용할 수 있습니다.
signal
getter 단계:
-
this.[[abortController]]의 signal을 반환한다.
error(e)
메서드 단계:
-
state를 this.[[stream]].[[state]]로 한다.
-
state가 "
writable
"이 아니면 반환한다. -
! WritableStreamDefaultControllerError(this, e)를 수행한다.
5.4.4. 내부 메서드
다음은 각 WritableStreamDefaultController
인스턴스가 구현하는 내부 메서드입니다.
writable stream의 구현체가 이 메서드들을 호출합니다.
이 메서드들이 abstract operation이 아닌 method 형식으로 존재하는 이유는 writable stream 구현과 controller 구현이 분리되어 있음을 명확히 하기 위해서이며, 앞으로 이러한 내부 메서드를 구현하는 다른 controller와 함께 확장될 수도 있음을 보여줍니다. readable stream의 경우에도 유사한 시나리오가 있으며(자세한 내용은 § 4.9.2 컨트롤러와의 인터페이스 참고), 실제로 여러 controller 타입이 존재하므로 대응되는 내부 메서드들이 다형적으로 사용됩니다.
-
result를 this.[[abortAlgorithm]]을 reason과 함께 실행한 결과로 한다.
-
! WritableStreamDefaultControllerClearAlgorithms(this)를 수행한다.
-
result를 반환한다.
-
! ResetQueue(this)를 수행한다.
5.5. 추상 연산
5.5.1. Writable stream 다루기
다음 추상 연산들은 WritableStream
인스턴스에서 더 높은 수준의 작업을 수행합니다.
-
writer를 새로운
WritableStreamDefaultWriter
로 한다. -
? SetUpWritableStreamDefaultWriter(writer, stream)를 수행한다.
-
writer를 반환한다.
-
단언: ! IsNonNegativeNumber(highWaterMark)는 true이다.
-
stream을 새로운
WritableStream
로 한다. -
! InitializeWritableStream(stream)을 수행한다.
-
controller를 새로운
WritableStreamDefaultController
로 한다. -
? SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm)를 수행한다.
-
stream을 반환한다.
이 추상 연산은 startAlgorithm이 throw할 경우에만 예외를 발생시킵니다.
-
stream.[[state]]를 "
writable
"로 설정한다. -
stream.[[storedError]], stream.[[writer]], stream.[[controller]], stream.[[inFlightWriteRequest]], stream.[[closeRequest]], stream.[[inFlightCloseRequest]], 그리고 stream.[[pendingAbortRequest]]를 undefined로 설정한다.
-
stream.[[writeRequests]]를 새로운 빈 list로 설정한다.
-
stream.[[backpressure]]를 false로 설정한다.
-
stream.[[writer]]가 undefined이면 false를 반환합니다.
-
true를 반환합니다.
-
! IsWritableStreamLocked(stream)가 true이면
TypeError
예외를 throw합니다. -
writer.[[stream]]을 stream으로 설정합니다.
-
stream.[[writer]]를 writer로 설정합니다.
-
state를 stream.[[state]]로 합니다.
-
state가 "
writable
"이면,-
! WritableStreamCloseQueuedOrInFlight(stream) 이 false이고 stream.[[backpressure]]가 true이면, writer.[[readyPromise]]를 새로운 promise로 설정합니다.
-
그 외의 경우, writer.[[readyPromise]]를 undefined로 resolve된 promise로 설정합니다.
-
writer.[[closedPromise]]를 새로운 promise로 설정합니다.
-
-
그 밖에 state가 "
erroring
"이면,-
writer.[[readyPromise]]를 stream의 [[storedError]]로 reject된 promise로 설정합니다.
-
writer.[[readyPromise]].[[PromiseIsHandled]] 를 true로 설정합니다.
-
writer.[[closedPromise]]를 새로운 promise로 설정합니다.
-
-
그 밖에 state가 "
closed
"이면,-
writer.[[readyPromise]]를 undefined로 resolve된 promise로 설정합니다.
-
writer.[[closedPromise]]를 undefined로 resolve된 promise로 설정합니다.
-
-
그 밖에,
-
단언: state는 "
errored
"이다. -
storedError를 stream.[[storedError]]로 한다.
-
writer.[[readyPromise]]를 storedError로 reject된 promise로 설정합니다.
-
writer.[[readyPromise]].[[PromiseIsHandled]] 를 true로 설정합니다.
-
writer.[[closedPromise]]를 storedError로 reject된 promise로 설정합니다.
-
writer.[[closedPromise]].[[PromiseIsHandled]] 를 true로 설정합니다.
-
-
stream.[[state]]가 "
closed
" 또는 "errored
"이면 undefined로 resolve된 promise를 반환합니다. -
abort 신호를 stream.[[controller]].[[abortController]] 에 reason과 함께 보냅니다.
-
state를 stream.[[state]]로 합니다.
-
state가 "
closed
" 또는 "errored
"이면 undefined로 resolve된 promise를 반환합니다.상태를 다시 확인하는 이유는 abort 신호가 작성자 코드를 실행시키며, 상태가 변경될 수 있기 때문입니다.
-
stream.[[pendingAbortRequest]]가 undefined가 아니면, stream.[[pendingAbortRequest]]의 promise를 반환합니다.
-
단언: state는 "
writable
" 또는 "erroring
"이다. -
wasAlreadyErroring을 false로 설정합니다.
-
state가 "
erroring
"이면,-
wasAlreadyErroring을 true로 설정합니다.
-
reason을 undefined로 설정합니다.
-
-
promise를 새로운 promise로 합니다.
-
stream.[[pendingAbortRequest]]를 새로운 pending abort request로 설정합니다. promise는 promise, reason은 reason, was already erroring은 wasAlreadyErroring로 합니다.
-
wasAlreadyErroring이 false인 경우, ! WritableStreamStartErroring(stream, reason)를 수행합니다.
-
promise를 반환합니다.
-
state를 stream.[[state]]로 합니다.
-
state가 "
closed
" 또는 "errored
"이면 TypeError 예외로 reject된 promise를 반환합니다. -
단언: state는 "
writable
" 또는 "erroring
"이다. -
단언: ! WritableStreamCloseQueuedOrInFlight(stream) 는 false이다.
-
promise를 새로운 promise로 합니다.
-
stream.[[closeRequest]]를 promise로 설정합니다.
-
writer를 stream.[[writer]]로 합니다.
-
writer가 undefined가 아니고, stream.[[backpressure]]가 true이며, state가 "
writable
"이면, resolve writer.[[readyPromise]] 를 undefined로 설정합니다. -
! WritableStreamDefaultControllerClose(stream.[[controller]])를 수행합니다.
-
promise를 반환합니다.
5.5.2. 컨트롤러와의 인터페이스
향후 default readable stream과 readable byte stream의 구분처럼 다양한 writable stream 동작을 추가할 수 있도록,
writable stream의 내부 상태
대부분은
WritableStreamDefaultController
클래스에 캡슐화되어 있습니다.
각 컨트롤러 클래스는 두 개의 내부 메서드를 정의하며, 이는 WritableStream
알고리즘에서 호출됩니다:
- [[AbortSteps]](reason)
- 컨트롤러가 스트림이 중단(aborted)될 때 실행하는 단계로, 컨트롤러에 저장된 상태를 정리하고 underlying sink에 알립니다.
- [[ErrorSteps]]()
- 컨트롤러가 스트림이 에러 상태가 될 때 실행하는 단계로, 컨트롤러에 저장된 상태를 정리합니다.
(이 메서드들이 abstract operation이 아닌 내부 메서드로 정의된 이유는, WritableStream
알고리즘이 어떤 타입의 컨트롤러가 있든 분기 없이 다형적으로 호출할 수 있게 하기 위함입니다. 현재로서는
WritableStreamDefaultController
만 존재하지만, 이론적으로 확장 가능성을 염두에 둔 설계입니다.)
이 절의 나머지는 컨트롤러 구현이 연결된 WritableStream
객체에 영향을 줄 수 있도록 하는 추상 연산에 관한 내용입니다.
즉, 컨트롤러의 내부 상태 변화가 WritableStream
의
public API를 통해 개발자에게 드러나는 결과로 변환됩니다.
-
단언: ! IsWritableStreamLocked(stream)는 true이다.
-
단언: stream.[[state]]는 "
writable
"이다. -
promise를 새로운 promise로 한다.
-
Append promise를 stream.[[writeRequests]]에 추가한다.
-
promise를 반환한다.
-
stream.[[closeRequest]]가 undefined이고 stream.[[inFlightCloseRequest]]도 undefined이면, false를 반환한다.
-
true를 반환한다.
-
state를 stream.[[state]]로 한다.
-
state가 "
writable
"이면,-
! WritableStreamStartErroring(stream, error)를 수행한다.
-
반환한다.
-
-
단언: state는 "
erroring
"이다. -
! WritableStreamFinishErroring(stream)을 수행한다.
-
단언: stream.[[state]]는 "
erroring
"이다. -
단언: ! WritableStreamHasOperationMarkedInFlight(stream) 는 false이다.
-
stream.[[state]]를 "
errored
"로 설정한다. -
! stream.[[controller]].[[ErrorSteps]]()를 수행한다.
-
storedError를 stream.[[storedError]]로 한다.
-
For each writeRequest of stream.[[writeRequests]]:
-
Reject writeRequest를 storedError로 한다.
-
-
stream.[[writeRequests]]를 빈 list로 설정한다.
-
stream.[[pendingAbortRequest]]가 undefined이면,
-
! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream)를 수행한다.
-
반환한다.
-
-
abortRequest를 stream.[[pendingAbortRequest]]로 한다.
-
stream.[[pendingAbortRequest]]를 undefined로 설정한다.
-
abortRequest의 was already erroring가 true이면,
-
! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream)를 수행한다.
-
반환한다.
-
promise를 ! stream.[[controller]].[[AbortSteps]](abortRequest의 reason)로 한다.
-
Upon fulfillment of promise,
-
! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream)를 수행한다.
-
Upon rejection of promise with reason reason,
-
! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream)를 수행한다.
-
단언: stream.[[inFlightCloseRequest]]는 undefined가 아니다.
-
Resolve stream.[[inFlightCloseRequest]]를 undefined로 한다.
-
stream.[[inFlightCloseRequest]]를 undefined로 설정한다.
-
state를 stream.[[state]]로 한다.
-
단언: stream.[[state]]는 "
writable
" 또는 "erroring
"이다. -
state가 "
erroring
"이면,-
stream.[[storedError]]를 undefined로 한다.
-
stream.[[pendingAbortRequest]]가 undefined가 아니면,
-
Resolve stream.[[pendingAbortRequest]]의 promise를 undefined로 한다.
-
stream.[[pendingAbortRequest]]를 undefined로 한다.
-
-
-
stream.[[state]]를 "
closed
"로 설정한다. -
writer를 stream.[[writer]]로 한다.
-
writer가 undefined가 아니면, resolve writer.[[closedPromise]]를 undefined로 한다.
-
단언: stream.[[pendingAbortRequest]]는 undefined이다.
-
단언: stream.[[storedError]]는 undefined이다.
-
단언: stream.[[inFlightCloseRequest]]는 undefined가 아니다.
-
Reject stream.[[inFlightCloseRequest]]를 error로 한다.
-
stream.[[inFlightCloseRequest]]를 undefined로 설정한다.
-
단언: stream.[[state]]는 "
writable
" 또는 "erroring
"이다. -
stream.[[pendingAbortRequest]]가 undefined가 아니면,
-
Reject stream.[[pendingAbortRequest]]의 promise를 error로 한다.
-
stream.[[pendingAbortRequest]]를 undefined로 한다.
-
-
! WritableStreamDealWithRejection(stream, error)를 수행한다.
-
단언: stream.[[inFlightWriteRequest]]는 undefined가 아니다.
-
Resolve stream.[[inFlightWriteRequest]]를 undefined로 한다.
-
stream.[[inFlightWriteRequest]]를 undefined로 한다.
-
단언: stream.[[inFlightWriteRequest]]는 undefined가 아니다.
-
Reject stream.[[inFlightWriteRequest]]를 error로 한다.
-
stream.[[inFlightWriteRequest]]를 undefined로 한다.
-
단언: stream.[[state]]는 "
writable
" 또는 "erroring
"이다. -
! WritableStreamDealWithRejection(stream, error)를 수행한다.
-
stream.[[inFlightWriteRequest]]가 undefined이고 stream.[[inFlightCloseRequest]]도 undefined이면, false를 반환한다.
-
true를 반환한다.
-
단언: stream.[[inFlightCloseRequest]]는 undefined이다.
-
단언: stream.[[closeRequest]]는 undefined가 아니다.
-
stream.[[inFlightCloseRequest]]를 stream.[[closeRequest]]로 설정한다.
-
stream.[[closeRequest]]를 undefined로 한다.
-
단언: stream.[[inFlightWriteRequest]]는 undefined이다.
-
단언: stream.[[writeRequests]]는 비어 있지 않다.
-
writeRequest를 stream.[[writeRequests]][0]로 한다.
-
Remove writeRequest를 stream.[[writeRequests]]에서 제거한다.
-
stream.[[inFlightWriteRequest]]를 writeRequest로 한다.
-
단언: stream.[[state]]는 "
errored
"이다. -
stream.[[closeRequest]]가 undefined가 아니면,
-
단언: stream.[[inFlightCloseRequest]]는 undefined이다.
-
Reject stream.[[closeRequest]]를 stream.[[storedError]]로 한다.
-
stream.[[closeRequest]]를 undefined로 한다.
-
-
writer를 stream.[[writer]]로 한다.
-
writer가 undefined가 아니면,
-
Reject writer.[[closedPromise]]를 stream.[[storedError]]로 한다.
-
writer.[[closedPromise]].[[PromiseIsHandled]] 를 true로 설정한다.
-
-
단언: stream.[[storedError]]는 undefined이다.
-
단언: stream.[[state]]는 "
writable
"이다. -
controller를 stream.[[controller]]로 한다.
-
단언: controller는 undefined가 아니다.
-
stream.[[state]]를 "
erroring
"로 설정한다. -
stream.[[storedError]]를 reason으로 한다.
-
writer를 stream.[[writer]]로 한다.
-
writer가 undefined가 아니면, ! WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason)를 수행한다.
-
! WritableStreamHasOperationMarkedInFlight(stream) 이 false이고 controller.[[started]]가 true이면, ! WritableStreamFinishErroring(stream)을 수행한다.
-
단언: stream.[[state]]는 "
writable
"이다. -
단언: ! WritableStreamCloseQueuedOrInFlight(stream) 는 false이다.
-
writer를 stream.[[writer]]로 한다.
-
writer가 undefined가 아니고 backpressure가 stream.[[backpressure]]와 다르면,
-
backpressure가 true이면, writer.[[readyPromise]]를 새로운 promise로 설정한다.
-
그 외의 경우,
-
단언: backpressure는 false이다.
-
Resolve writer.[[readyPromise]]를 undefined로 한다.
-
-
-
stream.[[backpressure]]를 backpressure로 설정한다.
5.5.3. Writer
다음 추상 연산은
WritableStreamDefaultWriter
인스턴스의 구현과 조작을 지원합니다.
-
stream을 writer.[[stream]]으로 한다.
-
단언: stream은 undefined가 아니다.
-
! WritableStreamAbort(stream, reason)을 반환한다.
-
stream을 writer.[[stream]]으로 한다.
-
단언: stream은 undefined가 아니다.
-
! WritableStreamClose(stream)을 반환한다.
-
stream을 writer.[[stream]]으로 한다.
-
단언: stream은 undefined가 아니다.
-
state를 stream.[[state]]로 한다.
-
! WritableStreamCloseQueuedOrInFlight(stream) 가 true이거나 state가 "
closed
"이면 undefined로 resolve된 promise를 반환한다. -
state가 "
errored
"이면, stream의 [[storedError]]로 reject된 promise를 반환한다. -
단언: state는 "
writable
" 또는 "erroring
"이다. -
! WritableStreamDefaultWriterClose(writer)를 반환한다.
이 추상 연산은
ReadableStream
의
pipeTo()
에러 전파 의미론 구현에 도움을 줍니다.
-
writer.[[closedPromise]].[[PromiseState]] 가 "
pending
"이면, reject writer.[[closedPromise]]를 error로 한다. -
그 외의 경우, writer.[[closedPromise]]를 error로 reject된 promise로 설정한다.
-
writer.[[closedPromise]].[[PromiseIsHandled]] 를 true로 설정한다.
-
writer.[[readyPromise]].[[PromiseState]] 가 "
pending
"이면, reject writer.[[readyPromise]]를 error로 한다. -
그 외의 경우, writer.[[readyPromise]]를 error로 reject된 promise로 설정한다.
-
writer.[[readyPromise]].[[PromiseIsHandled]] 를 true로 설정한다.
-
stream을 writer.[[stream]]으로 한다.
-
state를 stream.[[state]]로 한다.
-
state가 "
errored
" 또는 "erroring
"이면 null를 반환한다. -
state가 "
closed
"이면 0을 반환한다. -
! WritableStreamDefaultControllerGetDesiredSize(stream.[[controller]])를 반환한다.
-
stream을 writer.[[stream]]으로 한다.
-
단언: stream은 undefined가 아니다.
-
단언: stream.[[writer]]는 writer이다.
-
releasedError를 새로운
TypeError
로 한다. -
! WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, releasedError)를 수행한다.
-
! WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, releasedError)를 수행한다.
-
stream.[[writer]]를 undefined로 한다.
-
writer.[[stream]]를 undefined로 한다.
-
stream을 writer.[[stream]]으로 한다.
-
단언: stream은 undefined가 아니다.
-
controller를 stream.[[controller]]로 한다.
-
chunkSize를 ! WritableStreamDefaultControllerGetChunkSize(controller, chunk)로 한다.
-
stream이 writer.[[stream]]과 다르면, TypeError 예외로 reject된 promise를 반환한다.
-
state를 stream.[[state]]로 한다.
-
state가 "
errored
"이면, stream의 [[storedError]]로 reject된 promise를 반환한다. -
! WritableStreamCloseQueuedOrInFlight(stream) 가 true이거나 state가 "
closed
"이면, TypeError 예외로 reject된 promise를 반환한다. (스트림이 닫히는 중 또는 닫힘 상태임을 나타냄) -
state가 "
erroring
"이면, stream의 [[storedError]]로 reject된 promise를 반환한다. -
단언: state는 "
writable
"이다. -
promise를 ! WritableStreamAddWriteRequest(stream)로 한다.
-
! WritableStreamDefaultControllerWrite(controller, chunk, chunkSize)를 수행한다.
-
promise를 반환한다.
5.5.4. 기본 컨트롤러
다음 추상 연산들은
WritableStreamDefaultController
클래스의 구현을 지원합니다.
-
단언: stream이 구현함
WritableStream
. -
단언: stream.[[controller]] 는 undefined이다.
-
controller.[[stream]]을 stream으로 설정한다.
-
stream.[[controller]]를 controller로 설정한다.
-
! ResetQueue(controller)를 수행한다.
-
controller.[[abortController]]를 새로운
AbortController
로 설정한다. -
controller.[[started]]를 false로 설정한다.
-
controller.[[strategySizeAlgorithm]] 를 sizeAlgorithm으로 설정한다.
-
controller.[[strategyHWM]]를 highWaterMark로 설정한다.
-
controller.[[writeAlgorithm]]을 writeAlgorithm으로 설정한다.
-
controller.[[closeAlgorithm]]을 closeAlgorithm으로 설정한다.
-
controller.[[abortAlgorithm]]을 abortAlgorithm으로 설정한다.
-
backpressure를 ! WritableStreamDefaultControllerGetBackpressure(controller)로 한다.
-
! WritableStreamUpdateBackpressure(stream, backpressure)를 수행한다.
-
startResult를 startAlgorithm을 수행한 결과로 한다. (예외가 발생할 수도 있음)
-
startPromise를 startResult로 resolve된 promise로 한다.
-
-
단언: stream.[[state]]는 "
writable
" 또는 "erroring
"이다. -
controller.[[started]]를 true로 설정한다.
-
! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller)를 수행한다.
-
-
startPromise가 reject되면, 이유 r와 함께,
-
단언: stream.[[state]]는 "
writable
" 또는 "erroring
"이다. -
controller.[[started]]를 true로 설정한다.
-
! WritableStreamDealWithRejection(stream, r)를 수행한다.
-
-
controller를 새로운
WritableStreamDefaultController
로 한다. -
startAlgorithm을 undefined를 반환하는 알고리즘으로 한다.
-
writeAlgorithm을 undefined로 resolve된 promise를 반환하는 알고리즘으로 한다.
-
closeAlgorithm을 undefined로 resolve된 promise를 반환하는 알고리즘으로 한다.
-
abortAlgorithm을 undefined로 resolve된 promise를 반환하는 알고리즘으로 한다.
-
underlyingSinkDict["
start
"] 가 존재하면, startAlgorithm을 invoke 하여 underlyingSinkDict["start
"] 에 인자 « controller »를 전달하고, 예외 동작은 "rethrow
", callback this value는 underlyingSink로 하는 알고리즘으로 설정한다. -
underlyingSinkDict["
write
"] 가 존재하면, writeAlgorithm을 인자 chunk를 받아 invoke 하여 underlyingSinkDict["write
"] 에 인자 « chunk, controller »를 전달하고, callback this value는 underlyingSink로 하는 알고리즘으로 설정한다. -
underlyingSinkDict["
close
"] 가 존재하면, closeAlgorithm을 invoke 하여 underlyingSinkDict["close
"] 에 인자 리스트 «»를 전달하고, callback this value는 underlyingSink로 하는 알고리즘으로 설정한다. -
underlyingSinkDict["
abort
"] 가 존재하면, abortAlgorithm을 인자 reason를 받아 invoke 하여 underlyingSinkDict["abort
"] 에 인자 리스트 « reason »를 전달하고, callback this value는 underlyingSink로 하는 알고리즘으로 설정한다. -
? SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm)를 수행한다.
-
stream을 controller.[[stream]]로 한다.
-
controller.[[started]]가 false이면, 반환한다.
-
stream.[[inFlightWriteRequest]]가 undefined가 아니면, 반환한다.
-
state를 stream.[[state]]로 한다.
-
단언: state는 "
closed
" 또는 "errored
"가 아니다. -
state가 "
erroring
"이면,-
! WritableStreamFinishErroring(stream)를 수행한다.
-
반환한다.
-
-
controller.[[queue]]가 비어 있으면, 반환한다.
-
value를 ! PeekQueueValue(controller)로 한다.
-
value가 close sentinel이면, ! WritableStreamDefaultControllerProcessClose(controller)를 수행한다.
-
그 외의 경우, ! WritableStreamDefaultControllerProcessWrite(controller, value)를 수행한다.
WritableStream
자체가 참조되고 있더라도 가비지 컬렉션될 수 있습니다.
이는 약한 참조(weak references)를 통해 관찰할 수 있습니다. 자세한 내용은 tc39/proposal-weakrefs#31를 참고하십시오.
다음 단계를 수행합니다:
-
controller.[[writeAlgorithm]]을 undefined로 설정한다.
-
controller.[[closeAlgorithm]]을 undefined로 설정한다.
-
controller.[[abortAlgorithm]]을 undefined로 설정한다.
-
controller.[[strategySizeAlgorithm]] 을 undefined로 설정한다.
이 알고리즘은 일부 극단적인 상황에서는 여러 번 실행될 수 있습니다. 첫 실행 이후에는 아무 동작도 하지 않습니다.
-
! EnqueueValueWithSize(controller, close sentinel, 0)을 수행한다.
-
! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller)를 수행한다.
-
stream을 controller.[[stream]]으로 한다.
-
단언: stream.[[state]]는 "
writable
"이다. -
! WritableStreamDefaultControllerClearAlgorithms(controller)를 수행한다.
-
! WritableStreamStartErroring(stream, error)를 수행한다.
-
controller.[[stream]].[[state]]가 "
writable
"이면, ! WritableStreamDefaultControllerError(controller, error)를 수행한다.
-
desiredSize를 ! WritableStreamDefaultControllerGetDesiredSize(controller)로 한다.
-
desiredSize ≤ 0이면 true, 그렇지 않으면 false를 반환한다.
-
controller.[[strategySizeAlgorithm]] 이 undefined이면:
-
단언: controller.[[stream]].[[state]]는 "
writable
"가 아니다. -
1을 반환한다.
-
-
returnValue를 controller.[[strategySizeAlgorithm]]을 chunk에 대해 실행한 결과로 한다. 결과는 완료 레코드(completion record)로 해석한다.
-
returnValue가 abrupt completion이면,
-
! WritableStreamDefaultControllerErrorIfNeeded(controller, returnValue.[[Value]])를 수행한다.
-
1을 반환한다.
-
-
returnValue.[[Value]]를 반환한다.
-
controller.[[strategyHWM]] − controller.[[queueTotalSize]]를 반환한다.
-
stream을 controller.[[stream]]으로 한다.
-
! WritableStreamMarkCloseRequestInFlight(stream)를 수행한다.
-
! DequeueValue(controller)를 수행한다.
-
단언: controller.[[queue]]는 비어 있다.
-
sinkClosePromise를 controller.[[closeAlgorithm]]을 실행한 결과로 한다.
-
! WritableStreamDefaultControllerClearAlgorithms(controller)를 수행한다.
-
-
! WritableStreamFinishInFlightClose(stream)를 수행한다.
-
-
sinkClosePromise가 reject되면, 이유 reason과 함께,
-
! WritableStreamFinishInFlightCloseWithError(stream, reason)를 수행한다.
-
-
stream을 controller.[[stream]]으로 한다.
-
! WritableStreamMarkFirstWriteRequestInFlight(stream)를 수행한다.
-
sinkWritePromise를 controller.[[writeAlgorithm]]에 chunk를 넘겨 실행한 결과로 한다.
-
-
! WritableStreamFinishInFlightWrite(stream)를 수행한다.
-
state를 stream.[[state]]로 한다.
-
단언: state는 "
writable
" 또는 "erroring
"이다. -
! DequeueValue(controller)를 수행한다.
-
! WritableStreamCloseQueuedOrInFlight(stream) 가 false이고 state가 "
writable
"이면,-
backpressure를 ! WritableStreamDefaultControllerGetBackpressure(controller)로 한다.
-
! WritableStreamUpdateBackpressure(stream, backpressure)를 수행한다.
-
-
! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller)를 수행한다.
-
-
sinkWritePromise가 reject되면, 이유 reason과 함께,
-
stream.[[state]]가 "
writable
"이면, ! WritableStreamDefaultControllerClearAlgorithms(controller)를 수행한다. -
! WritableStreamFinishInFlightWriteWithError(stream, reason)를 수행한다.
-
-
enqueueResult를 EnqueueValueWithSize(controller, chunk, chunkSize)로 한다.
-
enqueueResult가 abrupt completion이면,
-
! WritableStreamDefaultControllerErrorIfNeeded(controller, enqueueResult.[[Value]])를 수행한다.
-
반환한다.
-
-
stream을 controller.[[stream]]으로 한다.
-
! WritableStreamCloseQueuedOrInFlight(stream) 가 false이고 stream.[[state]]가 "
writable
"이면,-
backpressure를 ! WritableStreamDefaultControllerGetBackpressure(controller)로 한다.
-
! WritableStreamUpdateBackpressure(stream, backpressure)를 수행한다.
-
-
! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller)를 수행한다.
6. 변환 스트림(Transform streams)
6.1. 변환 스트림 사용하기
readableStream. pipeThrough( transformStream) . pipeTo( writableStream) . then(() => console. log( "모든 데이터가 성공적으로 변환되었습니다!" )) . catch ( e=> console. error( "문제가 발생했습니다!" , e));
readable
및
writable
프로퍼티를 직접 사용하여 읽기 스트림(readable
stream)과 쓰기
스트림(writable stream)의 일반적인 인터페이스에 접근할 수 있습니다. 이 예제에서는 스트림의 쓰기 측(writable side)에 데이터를 writer 인터페이스를 통해 공급합니다. 읽기 측(readable side)은
anotherWritableStream
에 파이프됩니다.
const writer= transformStream. writable. getWriter(); writer. write( "입력 청크" ); transformStream. readable. pipeTo( anotherWritableStream);
fetch()
API는 읽기 스트림
요청 본문(request body)을 받아들이지만, 업로드를 위해 데이터를 쓰기 스트림 인터페이스로 작성하는 것이 더
편리할 수 있습니다. 동일 변환 스트림을 사용하면 이를 해결할 수 있습니다.
const { writable, readable} = new TransformStream(); fetch( "..." , { body: readable}). then( response=> /* ... */ ); const writer= writable. getWriter(); writer. write( new Uint8Array([ 0x73 , 0x74 , 0x72 , 0x65 , 0x61 , 0x6D , 0x73 , 0x21 ])); writer. close();
동일 변환 스트림의 또 다른 용도는 파이프에 추가 버퍼링을 제공하는 것입니다. 이 예제에서는 readableStream
과
writableStream
사이에 추가 버퍼링을 넣습니다.
const writableStrategy= new ByteLengthQueuingStrategy({ highWaterMark: 1024 * 1024 }); readableStream. pipeThrough( new TransformStream( undefined , writableStrategy)) . pipeTo( writableStream);
6.2. TransformStream
클래스
TransformStream
클래스는 일반적인 변환 스트림(transform
stream) 개념의 구체적인 인스턴스입니다.
6.2.1. 인터페이스 정의
TransformStream
클래스의 Web IDL 정의는 다음과 같습니다:
[Exposed=*,Transferable ]interface {
TransformStream constructor (optional object ,
transformer optional QueuingStrategy = {},
writableStrategy optional QueuingStrategy = {});
readableStrategy readonly attribute ReadableStream readable ;readonly attribute WritableStream writable ; };
6.2.2. 내부 슬롯
TransformStream
인스턴스는 아래 표에 설명된 내부 슬롯들과 함께 생성됩니다:
내부 슬롯 | 설명 (비규범적) |
---|---|
[[backpressure]] | [[readable]]에서 마지막으로 관찰되었을 때 역압(backpressure)이 있었는지 여부 |
[[backpressureChangePromise]] | [[backpressure]] 값이 변할 때마다 fulfill되고 새로 생성되는 promise |
[[controller]] | TransformStreamDefaultController
인스턴스로, [[readable]] 및 [[writable]]을 제어할
수 있는 능력을 가짐
|
[[Detached]] | 스트림이 전송될 때 true로 설정되는 불리언 플래그 |
[[readable]] | 이 객체가 제어하는 ReadableStream
인스턴스
|
[[writable]] | 이 객체가 제어하는 WritableStream
인스턴스
|
6.2.3. 트랜스포머(transformer) API
TransformStream()
생성자는 첫 번째 인자로 트랜스포머(transformer)를
나타내는 JavaScript 객체를 받습니다. 이러한 객체는 다음 메서드 중 아무거나 포함할 수 있습니다:
dictionary {
Transformer TransformerStartCallback start ;TransformerTransformCallback transform ;TransformerFlushCallback flush ;TransformerCancelCallback cancel ;any readableType ;any writableType ; };callback =
TransformerStartCallback any (TransformStreamDefaultController );
controller callback =
TransformerFlushCallback Promise <undefined > (TransformStreamDefaultController );
controller callback =
TransformerTransformCallback Promise <undefined > (any ,
chunk TransformStreamDefaultController );
controller callback =
TransformerCancelCallback Promise <undefined > (any );
reason
start(controller)
, 타입 TransformerStartCallback-
TransformStream
생성 시 즉시 호출되는 함수입니다.일반적으로 이 함수는
controller.enqueue()
를 사용하여 프리픽스(prefix) 청크를 큐잉하는 데 사용됩니다. 이러한 청크는 읽기 측에서 읽히지만 쓰기 측에 쓰기와는 무관하게 생성됩니다.이 초기 과정이 비동기인 경우(예: 프리픽스 청크를 가져오는 데 시간이 걸리는 경우), 성공 또는 실패를 알리기 위해 promise를 반환할 수 있습니다. promise가 reject되면 스트림이 에러 상태가 됩니다. 예외가 발생하면
TransformStream()
생성자가 예외를 다시 던집니다. transform(chunk, controller)
, 타입 TransformerTransformCallback-
쓰기 측(writable side)에 원래 쓰여진 새로운 청크(chunk)가 변환 준비가 되었을 때 호출되는 함수입니다. 스트림 구현은 이전 변환이 성공한 후에만 이 함수가 호출되도록 보장하며,
start()
가 완료되기 전이나flush()
가 호출된 후에는 절대 호출되지 않습니다.이 함수는 변환 스트림의 실제 변환 작업을 수행합니다.
controller.enqueue()
를 사용하여 결과를 큐잉할 수 있습니다. 이를 통해 쓰기 측에 쓰인 하나의 청크가 읽기 측에서 0개 또는 여러 개의 청크로 변환될 수 있습니다(큐잉 횟수에 따라 달라짐). § 10.9 템플릿 태그를 치환하는 변환 스트림 예제에서 0개의 청크를 큐잉하는 경우가 설명되어 있습니다.변환 과정이 비동기적이라면, 성공 또는 실패를 알리기 위해 promise를 반환할 수 있습니다. promise가 reject되면 읽기 측과 쓰기 측 모두 에러 상태가 됩니다.
이 함수가 반환하는 promise는 올바른 동작을 하는 프로듀서(producer)가 청크가 완전히 변환되기 전에 변경하지 않도록 보장해 줍니다. (이는 명세 기계가 보장하는 것이 아니라 프로듀서와 트랜스포머 간의 비공식적인 약속입니다.)
transform()
메서드를 제공하지 않으면, 동일 변환이 적용되어 쓰기 측의 청크가 그대로 읽기 측으로 큐잉됩니다. flush(controller)
, 타입 TransformerFlushCallback-
쓰기 측에 쓰여진 모든 청크가
transform()
을 통해 성공적으로 변환된 후, 쓰기 측이 닫히기 직전에 호출되는 함수입니다.일반적으로 이 함수는 읽기 측에 서픽스(suffix) 청크를 큐잉하는 데 사용되며, 그 후 읽기 측도 닫힙니다. 예시는 § 10.9 템플릿 태그를 치환하는 변환 스트림 예제에서 볼 수 있습니다.
플러시 과정이 비동기적이라면, 성공 또는 실패를 알리기 위해 promise를 반환할 수 있습니다. 결과는
stream.writable.write()
의 호출자에게 전달됩니다. 또한, promise가 reject되면 읽기 측과 쓰기 측 모두 에러 상태가 됩니다. 예외 발생도 reject된 promise와 동일하게 처리됩니다.(참고:
controller.terminate()
를flush()
내에서 호출할 필요는 없습니다. 스트림은 이미 성공적으로 종료되는 과정에 있으므로 terminate를 호출하는 것은 불필요합니다.) cancel(reason)
, 타입 TransformerCancelCallback-
읽기 측이 취소되거나 쓰기 측이 중단(aborted)될 때 호출되는 함수입니다.
일반적으로 이 함수는 스트림이 중단(aborted) 또는 취소(cancelled)될 때 트랜스포머의 하위 리소스를 정리하는 데 사용됩니다.
취소 과정이 비동기적이라면, 성공 또는 실패를 알리기 위해 promise를 반환할 수 있습니다. 결과는
stream.writable.abort()
또는stream.readable.cancel()
호출자에게 전달됩니다. 예외 발생도 reject된 promise와 동일하게 처리됩니다.(참고:
controller.terminate()
를cancel()
내에서 호출할 필요는 없습니다. 스트림은 이미 취소 또는 중단되는 과정에 있으므로 terminate를 호출하는 것은 불필요합니다.) readableType
, 타입 any-
이 프로퍼티는 향후 사용을 위해 예약되어 있으므로 값을 지정하려 하면 예외가 발생합니다.
writableType
, 타입 any-
이 프로퍼티는 향후 사용을 위해 예약되어 있으므로 값을 지정하려 하면 예외가 발생합니다.
controller
객체는 start()
,
transform()
,
flush()
에 전달되며,
TransformStreamDefaultController
인스턴스로, 청크를 읽기 측에 큐잉하거나 스트림을 종료 또는 에러 상태로 만들 수 있습니다.
6.2.4. 생성자 및 프로퍼티
stream = new
TransformStream
([transformer[, writableStrategy[, readableStrategy]]])-
제공된 transformer를 감싸는 새로운
TransformStream
인스턴스를 생성합니다. transformer 인자에 대한 자세한 내용은 § 6.2.3 트랜스포머 API를 참조하세요.transformer 인자가 제공되지 않으면 동일 변환 스트림(identity transform stream)이 생성됩니다. 유용한 사용 예시는 이 예제를 참고하세요.
writableStrategy와 readableStrategy 인자는 각각 쓰기 측과 읽기 측의 큐잉 전략(queuing strategy) 객체입니다. 이들은
WritableStream
및ReadableStream
객체 생성에 사용되어 변환 속도의 변동을 완화하거나 파이프(pipe) 내 버퍼링을 늘릴 수 있습니다. 제공되지 않으면 각각 high water mark가 1과 0인CountQueuingStrategy
와 동일하게 동작합니다. readable = stream.
readable
-
이 변환 스트림의 읽기 측을 나타내는
ReadableStream
을 반환합니다. writable = stream.
writable
-
이 변환 스트림의 쓰기 측을 나타내는
WritableStream
을 반환합니다.
new TransformStream(transformer, writableStrategy,
readableStrategy)
생성자 단계는 다음과 같습니다:
-
transformer가 없으면 null로 설정한다.
-
transformerDict를 transformer를
Transformer
타입의 IDL 값으로 변환한 값으로 한다.인자 transformer에
Transformer
타입을 직접 지정할 수 없는 이유는 원래 객체 참조를 잃기 때문입니다. 원래 객체를 유지해야 메서드 호출이 가능해집니다. -
transformerDict["
readableType
"] 가 존재하면,RangeError
예외를 throw한다. -
transformerDict["
writableType
"] 가 존재하면,RangeError
예외를 throw한다. -
readableHighWaterMark를 ? ExtractHighWaterMark(readableStrategy, 0)로 한다.
-
readableSizeAlgorithm을 ! ExtractSizeAlgorithm(readableStrategy)로 한다.
-
writableHighWaterMark를 ? ExtractHighWaterMark(writableStrategy, 1)로 한다.
-
writableSizeAlgorithm을 ! ExtractSizeAlgorithm(writableStrategy)로 한다.
-
startPromise를 새로운 promise로 한다.
-
! InitializeTransformStream(this, startPromise, writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark, readableSizeAlgorithm)를 수행한다.
-
? SetUpTransformStreamDefaultControllerFromTransformer(this, transformer, transformerDict)를 수행한다.
-
transformerDict["
start
"] 가 존재하면, resolve startPromise with invoke transformerDict["start
"] 에 인자 리스트 « this.[[controller]] »와 callback this value transformer로 호출한 결과로 한다. -
그 외의 경우, resolve startPromise를 undefined로 한다.
readable
getter 단계는 다음과 같습니다:
-
this.[[readable]]를 반환한다.
writable
getter 단계는 다음과 같습니다:
-
this.[[writable]]를 반환한다.
6.2.5. postMessage()
를 통한 전송
destination.postMessage(ts, { transfer: [ts] });
-
TransformStream
을 다른 프레임, 윈도우 또는 워커로 전송합니다.전송된 스트림은 원래 스트림과 동일하게 사용할 수 있습니다. 읽기 측과 쓰기 측은 잠기게(lock) 되어 직접 사용할 수 없습니다.
TransformStream
객체는 전송 가능한 객체(transferable objects)입니다. 전송 단계(transfer steps)는 value와 dataHolder가 주어졌을 때
다음과 같습니다:
-
readable을 value.[[readable]]로 한다.
-
writable을 value.[[writable]]로 한다.
-
! IsReadableStreamLocked(readable)가 true이면 "
DataCloneError
"DOMException
을 throw한다. -
! IsWritableStreamLocked(writable)가 true이면 "
DataCloneError
"DOMException
을 throw한다. -
dataHolder.[[readable]]를 ! StructuredSerializeWithTransfer(readable, « readable »)로 설정한다.
-
dataHolder.[[writable]]를 ! StructuredSerializeWithTransfer(writable, « writable »)로 설정한다.
-
readableRecord를 ! StructuredDeserializeWithTransfer(dataHolder.[[readable]], 현재 Realm)로 한다.
-
writableRecord를 ! StructuredDeserializeWithTransfer(dataHolder.[[writable]], 현재 Realm)로 한다.
-
value.[[readable]]를 readableRecord.[[Deserialized]]로 설정한다.
-
value.[[writable]]를 writableRecord.[[Deserialized]]로 설정한다.
-
value.[[backpressure]], value.[[backpressureChangePromise]], value.[[controller]]를 모두 undefined로 설정한다.
[[backpressure]],
[[backpressureChangePromise]], [[controller]] 슬롯은
전송된 TransformStream
에서는
사용되지 않습니다.
6.3.
TransformStreamDefaultController
클래스
TransformStreamDefaultController
클래스는 연관된 ReadableStream
및 WritableStream
을
조작할 수 있는 메서드를 제공합니다.
TransformStream
을
생성할 때,
transformer 객체에 해당하는 TransformStreamDefaultController
인스턴스가 주어져 조작할 수 있게 합니다.
6.3.1. 인터페이스 정의
TransformStreamDefaultController
클래스의 Web IDL 정의는 다음과 같습니다:
[Exposed=*]interface {
TransformStreamDefaultController readonly attribute unrestricted double ?desiredSize ;undefined enqueue (optional any );
chunk undefined error (optional any );
reason undefined terminate (); };
6.3.2. 내부 슬롯
TransformStreamDefaultController
인스턴스는 아래 표에 설명된 내부 슬롯들과 함께 생성됩니다:
내부 슬롯 | 설명 (비규범적) |
---|---|
[[cancelAlgorithm]] | 취소 이유 하나를 인자로 받아 transformer에 취소 요청을 전달하는 promise를 반환하는 알고리즘 |
[[finishPromise]] | [[cancelAlgorithm]]이나 [[flushAlgorithm]]이 완료될 때 resolve되는 promise. 값이 비어 있으면(undefined) 둘 중 어떤 알고리즘도 호출된 적이 없음을 의미함 |
[[flushAlgorithm]] | 닫기 요청을 transformer에 전달하는 promise를 반환하는 알고리즘 |
[[stream]] | 제어되는 TransformStream
인스턴스
|
[[transformAlgorithm]] | chunk 하나를 인자로 받아 transformer에 변환을 요청하는 promise를 반환하는 알고리즘 |
6.3.3. 메서드 및 프로퍼티
desiredSize = controller.
desiredSize
-
읽기 측 내부 큐를 채우기 위한 희망 크기(desired size)를 반환합니다. 큐가 과도하게 채워져 있으면 음수가 될 수 있습니다.
controller.
enqueue
(chunk)controller.
error
(e)-
제어 중인 변환 스트림의 읽기 측과 쓰기 측 모두를 에러 상태로 만들고, 이후 모든 상호작용이 주어진 에러 e로 실패하게 만듭니다. 변환 대기 중인 chunk들은 모두 버려집니다.
controller.
terminate
()-
제어 중인 변환 스트림의 읽기 측을 닫고, 쓰기 측을 에러 상태로 만듭니다. 이는 transformer가 쓰기 측에 쓰인 chunk 중 일부만 소비해야 할 때 유용합니다.
desiredSize
getter 단계는 다음과 같습니다:
-
readableController를 this.[[stream]].[[readable]].[[controller]]로 한다.
-
! ReadableStreamDefaultControllerGetDesiredSize(readableController)를 반환한다.
enqueue(chunk)
메서드 단계는 다음과 같습니다:
-
? TransformStreamDefaultControllerEnqueue(this, chunk)를 수행한다.
error(e)
메서드 단계는 다음과 같습니다:
-
? TransformStreamDefaultControllerError(this, e)를 수행한다.
terminate()
메서드 단계는 다음과 같습니다:
6.4. 추상 연산
6.4.1. 변환 스트림 다루기
다음 추상 연산들은 TransformStream
인스턴스에서 더 높은 수준의 작업을 수행합니다.
-
startAlgorithm을 startPromise를 반환하는 알고리즘으로 한다.
-
writeAlgorithm을 다음 단계로 한다. chunk 인자를 받음:
-
! TransformStreamDefaultSinkWriteAlgorithm(stream, chunk)를 반환한다.
-
-
abortAlgorithm을 다음 단계로 한다. reason 인자를 받음:
-
! TransformStreamDefaultSinkAbortAlgorithm(stream, reason)를 반환한다.
-
-
closeAlgorithm을 다음 단계로 한다:
-
! TransformStreamDefaultSinkCloseAlgorithm(stream)를 반환한다.
-
-
stream.[[writable]]를 ! CreateWritableStream(startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, writableHighWaterMark, writableSizeAlgorithm)로 설정한다.
-
pullAlgorithm을 다음 단계로 한다:
-
! TransformStreamDefaultSourcePullAlgorithm(stream)를 반환한다.
-
-
cancelAlgorithm을 다음 단계로 한다. reason 인자를 받음:
-
! TransformStreamDefaultSourceCancelAlgorithm(stream, reason)를 반환한다.
-
-
stream.[[readable]]를 ! CreateReadableStream(startAlgorithm, pullAlgorithm, cancelAlgorithm, readableHighWaterMark, readableSizeAlgorithm)로 설정한다.
-
stream.[[backpressure]]와 stream.[[backpressureChangePromise]]를 undefined로 설정한다.
[[backpressure]] 슬롯을 undefined로 설정하는 이유는 TransformStreamSetBackpressure에서 초기화하기 위해서입니다. 구현체에 따라 [[backpressure]]를 boolean으로 사용할 수도 있으며, 초기화 방식이 달라질 수 있습니다. 트랜스포머의
start()
메서드 호출 전에 올바르게 초기화만 된다면 사용자 코드에는 영향을 주지 않습니다. -
! TransformStreamSetBackpressure(stream, true)를 수행한다.
-
stream.[[controller]]를 undefined로 설정한다.
-
! ReadableStreamDefaultControllerError(stream.[[readable]].[[controller]], e)를 수행한다.
-
! TransformStreamErrorWritableAndUnblockWrite(stream, e)를 수행한다.
이 연산은 한쪽 또는 양쪽이 이미 에러 상태여도 올바르게 동작합니다. 따라서 에러 처리 시 알고리즘 호출자는 스트림 상태를 확인할 필요가 없습니다.
-
! TransformStreamDefaultControllerClearAlgorithms(stream.[[controller]])를 수행한다.
-
! WritableStreamDefaultControllerErrorIfNeeded(stream.[[writable]].[[controller]], e)를 수행한다.
-
! TransformStreamUnblockWrite(stream)를 수행한다.
-
단언: stream.[[backpressure]]가 backpressure와 다르다.
-
stream.[[backpressureChangePromise]]가 undefined가 아니면, resolve stream.[[backpressureChangePromise]]를 undefined로 설정한다.
-
stream.[[backpressureChangePromise]]를 새로운 promise로 설정한다.
-
stream.[[backpressure]]를 backpressure로 설정한다.
-
stream.[[backpressure]]가 true이면, ! TransformStreamSetBackpressure(stream, false)를 수행한다.
TransformStreamDefaultSinkWriteAlgorithm 추상 연산이 [[backpressureChangePromise]] 슬롯에 저장된 promise가 resolve되기를 기다릴 수 있습니다. TransformStreamSetBackpressure 호출은 promise가 항상 resolve되도록 보장합니다.
6.4.2. 기본 컨트롤러
다음 추상 연산들은
TransformStreamDefaultController
클래스의 구현을 지원합니다.
-
단언: stream이 구현함
TransformStream
. -
단언: stream.[[controller]]는 undefined이다.
-
controller.[[stream]]을 stream으로 설정한다.
-
stream.[[controller]]를 controller로 설정한다.
-
controller.[[transformAlgorithm]] 를 transformAlgorithm으로 설정한다.
-
controller.[[flushAlgorithm]]을 flushAlgorithm으로 설정한다.
-
controller.[[cancelAlgorithm]]을 cancelAlgorithm으로 설정한다.
-
controller를 새로운
TransformStreamDefaultController
로 한다. -
transformAlgorithm을 다음 단계로 한다. chunk 인자를 받음:
-
result를 TransformStreamDefaultControllerEnqueue(controller, chunk)로 한다.
-
result가 abrupt completion이면 result.[[Value]]로 reject된 promise를 반환한다.
-
그 외에는 undefined로 resolve된 promise를 반환한다.
-
-
flushAlgorithm을 undefined로 resolve된 promise를 반환하는 알고리즘으로 한다.
-
cancelAlgorithm을 undefined로 resolve된 promise를 반환하는 알고리즘으로 한다.
-
transformerDict["
transform
"] 가 존재하면, transformAlgorithm을 인자 chunk를 받아 invoke 하여 transformerDict["transform
"] 에 인자 리스트 « chunk, controller »를 전달하고, callback this value를 transformer로 하는 알고리즘으로 설정한다. -
transformerDict["
flush
"] 가 존재하면, flushAlgorithm을 invoke 하여 transformerDict["flush
"] 에 인자 리스트 « controller »를 전달하고, callback this value를 transformer로 하는 알고리즘으로 설정한다. -
transformerDict["
cancel
"] 가 존재하면, cancelAlgorithm을 인자 reason을 받아 invoke 하여 transformerDict["cancel
"] 에 인자 리스트 « reason »를 전달하고, callback this value를 transformer로 하는 알고리즘으로 설정한다. -
! SetUpTransformStreamDefaultController(stream, controller, transformAlgorithm, flushAlgorithm, cancelAlgorithm)를 수행한다.
TransformStream
자체가 참조되고 있더라도 가비지 컬렉션될 수 있습니다.
이는 약한 참조(weak references)를 통해 관찰할 수 있습니다. 자세한 내용은 tc39/proposal-weakrefs#31를 참고하세요.
다음 단계를 수행합니다:
-
controller.[[transformAlgorithm]]을 undefined로 설정한다.
-
controller.[[flushAlgorithm]]을 undefined로 설정한다.
-
controller.[[cancelAlgorithm]]을 undefined로 설정한다.
-
stream을 controller.[[stream]]으로 한다.
-
readableController를 stream.[[readable]].[[controller]]로 한다.
-
! ReadableStreamDefaultControllerCanCloseOrEnqueue(readableController) 가 false이면
TypeError
예외를 throw한다. -
enqueueResult를 ReadableStreamDefaultControllerEnqueue(readableController, chunk)로 한다.
-
enqueueResult가 abrupt completion이면,
-
! TransformStreamErrorWritableAndUnblockWrite(stream, enqueueResult.[[Value]])를 수행한다.
-
stream.[[readable]].[[storedError]]를 throw한다.
-
-
backpressure를 ! ReadableStreamDefaultControllerHasBackpressure(readableController)로 한다.
-
backpressure가 stream.[[backpressure]]와 다르면,
-
단언: backpressure는 true이다.
-
! TransformStreamSetBackpressure(stream, true)를 수행한다.
-
-
! TransformStreamError(controller.[[stream]], e)를 수행한다.
-
transformPromise를 controller.[[transformAlgorithm]]에 chunk를 전달하여 실행한 결과로 한다.
-
promise가 settle될 때 수행할 단계의 결과를 반환한다. rejection 단계의 인자 r에 대해:
-
! TransformStreamError(controller.[[stream]], r)를 수행한다.
-
r를 throw한다.
-
-
stream을 controller.[[stream]]으로 한다.
-
readableController를 stream.[[readable]].[[controller]]로 한다.
-
! ReadableStreamDefaultControllerClose(readableController)를 수행한다.
-
error를 "스트림이 종료되었습니다."를 나타내는
TypeError
예외로 한다. -
! TransformStreamErrorWritableAndUnblockWrite(stream, error)를 수행한다.
6.4.3. 기본 싱크
다음 추상 연산은 변환 스트림의 쓰기 측에 대한 underlying sink 구현에 사용됩니다.
-
단언: stream.[[writable]].[[state]]는 "
writable
"이다. -
controller를 stream.[[controller]]로 한다.
-
stream.[[backpressure]]가 true이면,
-
backpressureChangePromise를 stream.[[backpressureChangePromise]]로 한다.
-
단언: backpressureChangePromise는 undefined가 아니다.
-
promise가 settle될 때 단계의 결과를 반환한다. fulfill 단계:
-
writable을 stream.[[writable]]로 한다.
-
state를 writable.[[state]]로 한다.
-
state가 "
erroring
"이면 writable.[[storedError]]를 throw한다. -
단언: state는 "
writable
"이다. -
! TransformStreamDefaultControllerPerformTransform(controller, chunk)를 반환한다.
-
-
-
! TransformStreamDefaultControllerPerformTransform(controller, chunk)를 반환한다.
-
controller를 stream.[[controller]]로 한다.
-
controller.[[finishPromise]]가 undefined가 아니면, controller.[[finishPromise]]를 반환한다.
-
readable을 stream.[[readable]]로 한다.
-
controller.[[finishPromise]]를 새 promise로 한다.
-
cancelPromise를 controller.[[cancelAlgorithm]]에 reason을 전달하여 실행한 결과로 한다.
-
! TransformStreamDefaultControllerClearAlgorithms(controller)를 수행한다.
-
promise가 settle될 때 단계의 결과를 cancelPromise에 대해 수행한다:
-
cancelPromise가 fulfill되면:
-
readable.[[state]]가 "
errored
"이면, reject controller.[[finishPromise]] 를 readable.[[storedError]]와 함께 reject한다. -
그 외의 경우:
-
! ReadableStreamDefaultControllerError(readable.[[controller]], reason)를 수행한다.
-
Resolve controller.[[finishPromise]] 를 undefined로 resolve한다.
-
-
-
cancelPromise가 r 이유로 reject되면:
-
! ReadableStreamDefaultControllerError(readable.[[controller]], r)를 수행한다.
-
Reject controller.[[finishPromise]] 를 r로 reject한다.
-
-
-
controller.[[finishPromise]]를 반환한다.
-
controller를 stream.[[controller]]로 한다.
-
controller.[[finishPromise]]가 undefined가 아니면, controller.[[finishPromise]]를 반환한다.
-
readable을 stream.[[readable]]로 한다.
-
controller.[[finishPromise]]를 새 promise로 한다.
-
flushPromise를 controller.[[flushAlgorithm]]을 실행한 결과로 한다.
-
! TransformStreamDefaultControllerClearAlgorithms(controller)를 수행한다.
-
promise가 settle될 때 단계의 결과를 flushPromise에 대해 수행한다:
-
flushPromise가 fulfill되면:
-
readable.[[state]]가 "
errored
"이면, reject controller.[[finishPromise]] 를 readable.[[storedError]]로 reject한다. -
그 외의 경우:
-
! ReadableStreamDefaultControllerClose(readable.[[controller]])를 수행한다.
-
Resolve controller.[[finishPromise]] 를 undefined로 resolve한다.
-
-
-
flushPromise가 r 이유로 reject되면:
-
! ReadableStreamDefaultControllerError(readable.[[controller]], r)를 수행한다.
-
Reject controller.[[finishPromise]] 를 r로 reject한다.
-
-
-
controller.[[finishPromise]]를 반환한다.
6.4.4. 기본 소스
다음 추상 연산은 변환 스트림의 읽기 측에 대한 underlying source 구현에 사용됩니다.
-
controller를 stream.[[controller]]로 한다.
-
controller.[[finishPromise]]가 undefined가 아니면, controller.[[finishPromise]]를 반환한다.
-
writable을 stream.[[writable]]로 한다.
-
controller.[[finishPromise]]를 새 promise로 한다.
-
cancelPromise를 controller.[[cancelAlgorithm]]에 reason을 전달하여 실행한 결과로 한다.
-
! TransformStreamDefaultControllerClearAlgorithms(controller)를 수행한다.
-
promise가 settle될 때 단계의 결과를 cancelPromise에 대해 수행한다:
-
cancelPromise가 fulfill되면:
-
writable.[[state]]가 "
errored
"이면, reject controller.[[finishPromise]] 를 writable.[[storedError]]로 reject한다. -
그 외의 경우:
-
! WritableStreamDefaultControllerErrorIfNeeded(writable.[[controller]], reason)를 수행한다.
-
! TransformStreamUnblockWrite(stream)를 수행한다.
-
Resolve controller.[[finishPromise]] 를 undefined로 resolve한다.
-
-
-
cancelPromise가 r 이유로 reject되면:
-
! WritableStreamDefaultControllerErrorIfNeeded(writable.[[controller]], r)를 수행한다.
-
! TransformStreamUnblockWrite(stream)를 수행한다.
-
Reject controller.[[finishPromise]] 를 r로 reject한다.
-
-
-
controller.[[finishPromise]]를 반환한다.
-
단언: stream.[[backpressure]]는 true이다.
-
단언: stream.[[backpressureChangePromise]]는 undefined가 아니다.
-
! TransformStreamSetBackpressure(stream, false)를 수행한다.
-
stream.[[backpressureChangePromise]]를 반환한다.
7. 큐잉 전략
7.1. 큐잉 전략 API
ReadableStream()
,
WritableStream()
,
TransformStream()
생성자들은 모두
생성되는 스트림에 적합한 큐잉 전략을
나타내는 인자를 하나 이상 받습니다. 이러한 객체는 다음 프로퍼티를 포함합니다:
dictionary {
QueuingStrategy unrestricted double highWaterMark ;QueuingStrategySize size ; };callback =
QueuingStrategySize unrestricted double (any );
chunk
highWaterMark
, 유형 unrestricted double-
이 큐잉 전략을 사용하는 스트림의 최대 수위(high water mark)를 나타내는 음수가 아닌 숫자입니다.
size(chunk)
(바이트가 아닌 스트림 전용), 유형 QueuingStrategySize-
주어진 chunk 값의 유한하고 음수가 아닌 크기를 계산하여 반환하는 함수입니다.
결과값은 역압(backpressure)을 결정하는 데 사용되며, 해당하는
desiredSize
프로퍼티(예:defaultController.desiredSize
,byteController.desiredSize
,writer.desiredSize
등)에서 확인할 수 있습니다. 큐잉 전략이 사용되는 위치에 따라 다릅니다. 읽기 스트림의 경우에는 underlying source의pull()
메서드가 호출되는 시점도 결정합니다.이 함수는 멱등이어야 하고 부작용을 일으키지 않아야 합니다. 그렇지 않으면 매우 이상한 결과가 발생할 수 있습니다.
읽기 바이트 스트림(readable byte streams)의 경우에는 이 함수가 사용되지 않으며, 청크는 항상 바이트 단위로 측정됩니다.
이러한 프로퍼티를 가진 객체는 큐잉 전략 객체가 필요할 때 사용할 수 있습니다. 하지만, 몇 가지 경우에 사용할 수 있는 공통 용어를 제공하는 내장 큐잉 전략 클래스가 두 가지 있습니다:
ByteLengthQueuingStrategy
와 CountQueuingStrategy
입니다.
두 클래스 모두 생성자에서 다음 Web IDL 조각을 사용합니다:
dictionary {
QueuingStrategyInit required unrestricted double ; };
highWaterMark
7.2. ByteLengthQueuingStrategy
클래스
바이트 단위 작업 시 일반적으로 사용하는 큐잉
전략은 들어오는 청크(chunk)들의
byteLength
속성의 합이 지정한 최대 수위(high-water mark)에 도달할 때까지 대기하는 것입니다.
따라서 스트림을 생성할 때 사용할 수 있는 내장 큐잉 전략으로 제공됩니다.
const stream= new ReadableStream( { ... }, new ByteLengthQueuingStrategy({ highWaterMark: 16 * 1024 }) );
이 경우, 청크가 16 KiB만큼 읽기 스트림의 underlying source에 의해 큐잉될 수 있으며, 그 이후 읽기 스트림 구현이 역압(backpressure) 신호를 underlying source에 전송하기 시작합니다.
const stream= new WritableStream( { ... }, new ByteLengthQueuingStrategy({ highWaterMark: 32 * 1024 }) );
이 경우, 청크가 32 KiB만큼 쓰기 스트림 내부 큐에 쌓일 수 있으며, 이전에 쓰기 작업이 완료되기를 기다리게 됩니다. 그 이후 쓰기 스트림이 underlying sink에 신호를 보내기 시작합니다.
ByteLengthQueuingStrategy
를 읽기 바이트
스트림(readable byte streams)에 사용할 필요는 없습니다. 이들은 항상 바이트 단위로 청크를 측정합니다. ByteLengthQueuingStrategy로 바이트
스트림을 생성하려 하면 실패합니다.
7.2.1. 인터페이스 정의
ByteLengthQueuingStrategy
클래스의 Web IDL 정의는 다음과 같습니다:
[Exposed=*]interface {
ByteLengthQueuingStrategy constructor (QueuingStrategyInit );
init readonly attribute unrestricted double highWaterMark ;readonly attribute Function size ; };
7.2.2. 내부 슬롯
ByteLengthQueuingStrategy
인스턴스에는 생성자에서 받은 값을 저장하는 [[highWaterMark]]
내부 슬롯이 있습니다.
Function
타입의 값이며, 다음과 같이 초기화되어야 합니다:
-
chunk를 받아 다음 단계를 수행하는 steps를 정의한다:
-
? GetV(chunk, "
byteLength
")를 반환한다.
-
-
F를 ! CreateBuiltinFunction(steps, 1, "
size
", « », globalObject의 관련 Realm(relevant Realm))로 한다. -
globalObject의 byte length queuing strategy size function을
Function
타입의 값으로 설정하며, F를 참조하는 함수이고 콜백 컨텍스트(callback context)는 globalObject의 관련 설정 객체(relevant settings object)로 한다.
이 설계는 다소 역사적인 이유가 있습니다. size
가 this
값을 검사하지 않는 함수(메서드가 아닌 순수 함수)임을 보장하기 위해서입니다. 자세한 배경은 whatwg/streams#1005 및 heycam/webidl#819를 참고하세요.
7.2.3. 생성자 및 프로퍼티
strategy = new
ByteLengthQueuingStrategy
({highWaterMark
})-
제공된 최대 수위(high water mark)로 새로운
ByteLengthQueuingStrategy
를 생성합니다.제공된 최대 수위(high water mark)는 미리 검증되지 않습니다. 음수, NaN 또는 숫자가 아닌 경우, 생성된
ByteLengthQueuingStrategy
는 해당 스트림 생성자에서 예외를 발생시킬 것입니다. highWaterMark = strategy.
highWaterMark
-
생성자에 전달된 최대 수위(high water mark)를 반환합니다.
strategy.
size
(chunk)-
chunk의
byteLength
프로퍼티 값을 반환하여 크기를 측정합니다.
new ByteLengthQueuingStrategy(init)
생성자 단계는 다음과
같습니다:
-
this.[[highWaterMark]]를 init["
highWaterMark
"]로 설정한다.
highWaterMark
getter 단계는 다음과 같습니다:
-
this.[[highWaterMark]]를 반환한다.
size
getter 단계는 다음과 같습니다:
7.3. CountQueuingStrategy
클래스
일반 객체 스트림을 다룰 때 흔히 사용하는 큐잉 전략은 지금까지 누적된 청크의 개수만 세어서, 이 숫자가 지정된 최대 수위(high-water mark)에 도달할 때까지 기다리는 것입니다. 그래서 이 전략도 기본으로 제공합니다.
const stream= new ReadableStream( { ... }, new CountQueuingStrategy({ highWaterMark: 10 }) );
이 경우, 10개의 청크(종류 상관 없음)가 읽기 스트림의 underlying source에 의해 큐잉될 수 있으며, 그 이후 읽기 스트림 구현이 역압(backpressure) 신호를 underlying source에 전송하기 시작합니다.
const stream= new WritableStream( { ... }, new CountQueuingStrategy({ highWaterMark: 5 }) );
이 경우, 5개의 청크(종류 상관 없음)가 쓰기 스트림의 내부 큐에 쌓일 수 있으며, 이전 쓰기 작업이 완료되기를 기다리게 됩니다. 그 이후 쓰기 스트림이 underlying sink에 신호를 보내기 시작합니다.
7.3.1. 인터페이스 정의
CountQueuingStrategy
클래스의 Web IDL 정의는 다음과 같습니다:
[Exposed=*]interface {
CountQueuingStrategy constructor (QueuingStrategyInit );
init readonly attribute unrestricted double highWaterMark ;readonly attribute Function size ; };
7.3.2. 내부 슬롯
CountQueuingStrategy
인스턴스에는 생성자에서 받은 값을 저장하는 [[highWaterMark]] 내부 슬롯이 있습니다.
Function
타입의 값이며, 다음과 같이 초기화되어야 합니다:
-
다음 단계를 수행하는 steps를 정의한다:
-
1을 반환한다.
-
-
F를 ! CreateBuiltinFunction(steps, 0, "
size
", « », globalObject의 관련 Realm(relevant Realm))로 한다. -
globalObject의 count queuing strategy size function을
Function
타입의 값으로 설정하며, F를 참조하는 함수이고 콜백 컨텍스트(callback context)는 globalObject의 관련 설정 객체(relevant settings object)로 한다.
이 설계는 다소 역사적인 이유가 있습니다. size
가 this 값을
검사하지 않는 함수(메서드가 아닌 순수 함수)임을 보장하기 위해서입니다. 자세한 배경은 whatwg/streams#1005 및 heycam/webidl#819를 참고하세요.
7.3.3. 생성자 및 프로퍼티
strategy = new
CountQueuingStrategy
({highWaterMark
})-
제공된 최대 수위(high water mark)로 새로운
CountQueuingStrategy
를 생성합니다.제공된 최대 수위(high water mark)는 미리 검증되지 않습니다. 음수, NaN 또는 숫자가 아닌 경우, 생성된
CountQueuingStrategy
는 해당 스트림 생성자에서 예외를 발생시킬 것입니다. highWaterMark = strategy.
highWaterMark
-
생성자에 전달된 최대 수위(high water mark)를 반환합니다.
strategy.
size
(chunk)-
chunk의 크기를 항상 1로 반환하여, 큐의 총 크기가 큐 내 청크 개수임을 보장합니다.
new CountQueuingStrategy(init)
생성자 단계는 다음과 같습니다:
-
this.[[highWaterMark]]를 init["
highWaterMark
"]로 설정한다.
highWaterMark
getter 단계는 다음과 같습니다:
-
this.[[highWaterMark]]를 반환한다.
size
getter 단계는 다음과 같습니다:
7.4. 추상 연산
다음 알고리즘은 스트림 생성자에서 QueuingStrategy
딕셔너리에서 관련 부분을 추출하는 데 사용됩니다.
-
strategy["
highWaterMark
"] 가 존재하지 않으면, defaultHWM을 반환한다. -
highWaterMark를 strategy["
highWaterMark
"]로 한다. -
highWaterMark가 NaN이거나 highWaterMark < 0이면
RangeError
예외를 throw한다. -
highWaterMark를 반환한다.
+∞는 명시적으로 유효한 최대 수위(high water mark)로 허용됩니다. 이는 역압(backpressure) 이 절대 적용되지 않음을 의미합니다.
8. 추상 연산 지원
다음 추상 연산들은 하나 이상의 스트림 타입 구현을 지원하므로, 위의 주요 섹션 아래에 묶이지 않습니다.
8.1. 크기가 있는 큐(queue-with-sizes)
이 명세의 스트림들은 큐잉된 값과 그 크기를 함께 저장할 때 "크기가 있는 큐(queue-with-sizes)" 데이터 구조를 사용합니다. 다양한 명세 객체들은 항상 [[queue]]와
[[queueTotalSize]]라는 이름의 두 내부 슬롯을 쌍으로 가지고 있으며, [[queue]]는 리스트(list) 형태의 value-with-sizes이고,
[[queueTotalSize]]는 JavaScript Number
(즉, 배정밀도 부동소수점 숫자)입니다.
아래의 추상 연산들은 크기가 있는 큐(queue-with-sizes)를 포함하는 객체를 다룰 때 두 내부 슬롯의 동기화를 보장하기 위해 사용됩니다.
부동소수점 산술의 정밀도 한계로 인해, [[queueTotalSize]] 슬롯에 누적합을 저장하는 이 프레임워크는 반드시 [[queue]]에 있는 모든 청크의 크기를 합산하는 것과 동일하지는 않습니다. (하지만, 이는 청크 크기에 약 1015 정도의 큰 편차가 있거나, 수조 개의 청크가 큐잉되는 경우에만 차이가 발생합니다.)
이하에서 value-with-size는 struct로서, 두 개의 item value와 size를 포함합니다.
-
단언: container는 [[queue]]와 [[queueTotalSize]] 내부 슬롯을 가진다.
-
단언: container.[[queue]]는 비어 있지 않다.
-
valueWithSize를 container.[[queue]][0]로 한다.
-
Remove valueWithSize를 container.[[queue]]에서 제거한다.
-
container.[[queueTotalSize]]를 container.[[queueTotalSize]] − valueWithSize의 size로 설정한다.
-
container.[[queueTotalSize]] < 0이면 container.[[queueTotalSize]]를 0으로 설정한다. (이것은 반올림 오차 때문에 발생할 수 있습니다.)
-
valueWithSize의 value를 반환한다.
-
단언: container는 [[queue]]와 [[queueTotalSize]] 내부 슬롯을 가진다.
-
! IsNonNegativeNumber(size)가 false이면,
RangeError
예외를 throw한다. -
size가 +∞이면
RangeError
예외를 throw한다. -
Append 새로운 value-with-size (value: value, size: size)를 container.[[queue]]에 추가한다.
-
container.[[queueTotalSize]]를 container.[[queueTotalSize]] + size로 설정한다.
-
단언: container는 [[queue]]와 [[queueTotalSize]] 내부 슬롯을 가진다.
-
container.[[queue]]를 새로운 빈 리스트(list)로 설정한다.
-
container.[[queueTotalSize]]를 0으로 설정한다.
8.2. 전송 가능한 스트림
전송 가능한 스트림은 특수한 종류의 동일 변환(identity transform)을 사용하여 구현되며, 쓰기 측이 하나의 realm에, 읽기 측이 다른 realm에 위치하게 됩니다. 다음의 추상 연산들은 이러한 "cross-realm transform"을 구현하는 데 사용됩니다.
-
PackAndPostMessage(port, "
error
", error)를 수행하고, 결과는 무시한다.
이 추상 연산이 수행될 때 이미 오류 상태이므로, 추가 오류는 처리할 수 없으니 그냥 버립니다.
-
message를 OrdinaryObjectCreate(null)로 한다.
-
! CreateDataProperty(message, "
type
", type)를 수행한다. -
! CreateDataProperty(message, "
value
", value)를 수행한다. -
targetPort를 port와 연결(entangled)된 포트가 있으면 그 포트로, 없으면 null로 한다.
-
options를 «[ "
transfer
" → « » ]»로 한다. -
message port post message steps를 실행하여 targetPort, message, options를 전달한다.
전송 시 자바스크립트 객체를 사용하는 이유는 message port post message steps를 중복하지 않기 위해서입니다.
객체의 프로토타입을 null로 설정하는 이유는
%Object.prototype%
의
영향을 피하기 위함입니다.
-
result를 PackAndPostMessage(port, type, value)로 한다.
-
result가 abrupt completion이면,
-
! CrossRealmTransformSendError(port, result.[[Value]])를 수행한다.
-
-
result를 completion record로 반환한다.
-
! InitializeReadableStream(stream)를 수행한다.
-
controller를 새로운
ReadableStreamDefaultController
로 한다. -
port의
message
이벤트 핸들러를 아래 단계로 추가한다:-
data를 메시지의 데이터로 한다.
-
단언: data는 객체(Object)이다.
-
type을 ! Get(data, "
type
")로 한다. -
value를 ! Get(data, "
value
")로 한다. -
단언: type은 문자열(String)이다.
-
type이 "
chunk
"이면,-
! ReadableStreamDefaultControllerEnqueue(controller, value)를 수행한다.
-
-
그 외에 type이 "
close
"이면,-
! ReadableStreamDefaultControllerClose(controller)를 수행한다.
-
port를 분리(disentangle)한다.
-
-
그 외에 type이 "
error
"이면,-
! ReadableStreamDefaultControllerError(controller, value)를 수행한다.
-
port를 분리(disentangle)한다.
-
-
-
port의
messageerror
이벤트 핸들러를 아래 단계로 추가한다:-
error를 새로운 "
DataCloneError
"DOMException
로 한다. -
! CrossRealmTransformSendError(port, error)를 수행한다.
-
! ReadableStreamDefaultControllerError(controller, error)를 수행한다.
-
port를 분리(disentangle)한다.
-
-
port의 포트 메시지 큐(port message queue)를 활성화한다.
-
startAlgorithm을 undefined를 반환하는 알고리즘으로 한다.
-
pullAlgorithm을 다음 단계로 한다:
-
! PackAndPostMessage(port, "
pull
", undefined)를 수행한다. -
undefined로 resolve된 promise를 반환한다.
-
-
cancelAlgorithm을 다음 단계로 한다. reason 인자를 받음:
-
result를 PackAndPostMessageHandlingError(port, "
error
", reason)로 한다. -
port를 분리(disentangle)한다.
-
result가 abrupt completion이면 result.[[Value]]로 reject된 promise를 반환한다.
-
그 외에는 undefined로 resolve된 promise를 반환한다.
-
-
sizeAlgorithm을 1을 반환하는 알고리즘으로 한다.
-
! SetUpReadableStreamDefaultController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, 0, sizeAlgorithm)를 수행한다.
구현체에서는 이 알고리즘의 assert 실패를 명시적으로 처리하는 것이 권장됩니다. 입력이 신뢰할 수 없는 컨텍스트에서 올 수 있으므로, 그렇지 않으면 보안 문제가 발생할 수 있습니다.
-
! InitializeWritableStream(stream)를 수행한다.
-
controller를 새로운
WritableStreamDefaultController
로 한다. -
backpressurePromise를 새로운 promise로 한다.
-
port의
message
이벤트 핸들러를 아래 단계로 추가한다:-
data를 메시지의 데이터로 한다.
-
단언: data는 객체(Object)이다.
-
type을 ! Get(data, "
type
")로 한다. -
value를 ! Get(data, "
value
")로 한다. -
단언: type은 문자열(String)이다.
-
type이 "
pull
"이면,-
backpressurePromise가 undefined가 아니면,
-
Resolve backpressurePromise 를 undefined로 resolve한다.
-
backpressurePromise를 undefined로 설정한다.
-
-
-
그 외에 type이 "
error
"이면,-
! WritableStreamDefaultControllerErrorIfNeeded(controller, value)를 수행한다.
-
backpressurePromise가 undefined가 아니면,
-
Resolve backpressurePromise 를 undefined로 resolve한다.
-
backpressurePromise를 undefined로 설정한다.
-
-
-
-
port의
messageerror
이벤트 핸들러를 아래 단계로 추가한다:-
error를 새로운 "
DataCloneError
"DOMException
로 한다. -
! CrossRealmTransformSendError(port, error)를 수행한다.
-
! WritableStreamDefaultControllerErrorIfNeeded(controller, error)를 수행한다.
-
port를 분리(disentangle)한다.
-
-
port의 포트 메시지 큐(port message queue)를 활성화한다.
-
startAlgorithm을 undefined를 반환하는 알고리즘으로 한다.
-
writeAlgorithm을 다음 단계로 한다. chunk 인자를 받음:
-
backpressurePromise가 undefined이면 backpressurePromise를 undefined로 resolve된 promise로 설정한다.
-
promise가 settle될 때 단계의 결과를 반환한다. fulfill 단계:
-
backpressurePromise를 새로운 promise로 설정한다.
-
result를 PackAndPostMessageHandlingError(port, "
chunk
", chunk)로 한다. -
result가 abrupt completion이면,
-
port를 분리(disentangle)한다.
-
result.[[Value]]로 reject된 promise를 반환한다.
-
-
그 외에는 undefined로 resolve된 promise를 반환한다.
-
-
-
closeAlgorithm을 다음 단계로 한다:
-
! PackAndPostMessage(port, "
close
", undefined)를 수행한다. -
port를 분리(disentangle)한다.
-
undefined로 resolve된 promise를 반환한다.
-
-
abortAlgorithm을 다음 단계로 한다. reason 인자를 받음:
-
result를 PackAndPostMessageHandlingError(port, "
error
", reason)로 한다. -
port를 분리(disentangle)한다.
-
result가 abrupt completion이면 result.[[Value]]로 reject된 promise를 반환한다.
-
그 외에는 undefined로 resolve된 promise를 반환한다.
-
-
sizeAlgorithm을 1을 반환하는 알고리즘으로 한다.
-
! SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, 1, sizeAlgorithm)를 수행한다.
구현체에서는 이 알고리즘의 assert 실패를 명시적으로 처리하는 것이 권장됩니다. 입력이 신뢰할 수 없는 컨텍스트에서 올 수 있으므로, 그렇지 않으면 보안 문제가 발생할 수 있습니다.
8.3. 기타
아래 추상 연산들은 다양한 유틸리티 기능을 제공합니다.
-
단언: O는 객체(Object)이다.
-
단언: O는 [[ArrayBufferData]] 내부 슬롯을 가진다.
-
! IsDetachedBuffer(O)가 true이면 false를 반환한다.
-
SameValue(O.[[ArrayBufferDetachKey]], undefined) 가 false이면 false를 반환한다.
-
true를 반환한다.
-
v가 숫자(Number)가 아니면 false를 반환한다.
-
v가 NaN이면 false를 반환한다.
-
v < 0이면 false를 반환한다.
-
true를 반환한다.
-
단언: ! IsDetachedBuffer(O)가 false이다.
-
arrayBufferData를 O.[[ArrayBufferData]]로 한다.
-
arrayBufferByteLength를 O.[[ArrayBufferByteLength]]로 한다.
-
? DetachArrayBuffer(O)를 수행한다.
만약 O에 undefined가 아닌 [[ArrayBufferDetachKey]]가 있으면(WebAssembly.Memory의
WebAssembly.Memory
의buffer
등), 예외가 발생합니다. [WASM-JS-API-1] -
현재 Realm에서 arrayBufferData와 arrayBufferByteLength를 내부 슬롯 값으로 하는 새로운
ArrayBuffer
객체를 반환한다.
-
단언: O는 객체(Object)이다.
-
단언: O는 [[ViewedArrayBuffer]] 내부 슬롯을 가진다.
-
단언: ! IsDetachedBuffer(O.[[ViewedArrayBuffer]]) 가 false이다.
-
buffer를 ? CloneArrayBuffer(O.[[ViewedArrayBuffer]], O.[[ByteOffset]], O.[[ByteLength]],
%ArrayBuffer%
)로 한다. -
array를 ! Construct(
%Uint8Array%
, « buffer »)로 한다. -
array를 반환한다.
-
serialized를 ? StructuredSerialize(v)로 한다.
-
? StructuredDeserialize(serialized, 현재 Realm)을 반환한다.
-
단언: toBuffer는 객체(Object)이다.
-
단언: toBuffer는 [[ArrayBufferData]] 내부 슬롯을 가진다.
-
단언: fromBuffer는 객체(Object)이다.
-
단언: fromBuffer는 [[ArrayBufferData]] 내부 슬롯을 가진다.
-
toBuffer가 fromBuffer와 같으면 false를 반환한다.
-
! IsDetachedBuffer(toBuffer)가 true이면 false를 반환한다.
-
! IsDetachedBuffer(fromBuffer)가 true이면 false를 반환한다.
-
toIndex + count > toBuffer.[[ArrayBufferByteLength]]이면 false를 반환한다.
-
fromIndex + count > fromBuffer.[[ArrayBufferByteLength]]이면 false를 반환한다.
-
true를 반환한다.
9. 다른 명세에서 스트림 사용하기
이 현행 표준의 많은 내용은 스트림의 내부 동작에 초점을 맞추고 있습니다. 다른 명세들은 보통 이러한 세부사항을 신경쓸 필요가 없습니다. 대신, 이 표준에서 정의한 다양한 IDL 타입과 아래 정의들을 통해 인터페이스해야 합니다.
명세에서는 이 표준에서 정의한 다양한 내부 슬롯을 직접 검사하거나 조작하지 않아야 합니다. 마찬가지로, 여기 정의된 추상 연산을 사용해서도 안 됩니다. 이런 직접적인 사용은 이 표준이 보장하는 불변식을 깨뜨릴 수 있습니다.
만약 귀하의 명세에서 여기서 지원하지 않는 방식으로 스트림과 인터페이스하고 싶다면, 이슈를 등록하세요. 이 섹션은 필요에 따라 유기적으로 확장될 예정입니다.
9.1. 읽기 스트림(Readable streams)
9.1.1. 생성과 조작
ReadableStream
객체 stream을 설정(set up)하려면, 선택적으로 알고리즘 pullAlgorithm, 선택적으로
알고리즘 cancelAlgorithm, 선택적
숫자 highWaterMark (기본값 1),
선택적 알고리즘 sizeAlgorithm을 받아 아래 단계를 수행합니다.
pullAlgorithm과 cancelAlgorithm이 주어지면 promise를 반환할 수 있습니다. sizeAlgorithm이
주어지면 반드시 chunk 객체를 받아 숫자를 반환하는 알고리즘이어야 하고,
highWaterMark가 주어지면 반드시 음수가 아니고 NaN이 아닌 숫자이어야 합니다.
-
startAlgorithm을 undefined를 반환하는 알고리즘으로 한다.
-
pullAlgorithmWrapper를 아래 단계로 실행하는 알고리즘으로 한다:
-
result를 pullAlgorithm이 주어졌으면 실행 결과로, 아니면 null로 한다. 만약 이 과정에서 예외 e가 발생하면 e로 reject된 promise를 반환한다.
-
result가
Promise
이면, result를 반환한다. -
undefined로 resolve된 promise를 반환한다.
-
-
cancelAlgorithmWrapper를 reason을 받아 아래 단계로 실행하는 알고리즘으로 한다:
-
result를 cancelAlgorithm이 주어졌다면 reason을 전달하여 실행한 결과로, 아니면 null로 한다. 이 과정에서 예외 e가 발생하면 e로 reject된 promise를 반환한다.
-
result가
Promise
이면, result를 반환한다. -
undefined로 resolve된 promise를 반환한다.
-
-
sizeAlgorithm이 주어지지 않았다면 1을 반환하는 알고리즘으로 설정한다.
-
! InitializeReadableStream(stream)을 수행한다.
-
controller를 새로운
ReadableStreamDefaultController
로 한다. -
! SetUpReadableStreamDefaultController(stream, controller, startAlgorithm, pullAlgorithmWrapper, cancelAlgorithmWrapper, highWaterMark, sizeAlgorithm)를 수행한다.
ReadableStream
객체 stream을 바이트 읽기 지원으로 설정(set up with byte reading
support)하려면, 선택적으로 알고리즘
pullAlgorithm,
선택적으로 알고리즘 cancelAlgorithm,
선택적 숫자 highWaterMark
(기본값 0)를 받아 아래 단계를 수행합니다. pullAlgorithm과 cancelAlgorithm이 주어지면 promise를 반환할 수 있습니다.
highWaterMark가 주어지면 반드시 음수가 아니고 NaN이 아닌 숫자이어야 합니다.
-
startAlgorithm을 undefined를 반환하는 알고리즘으로 한다.
-
pullAlgorithmWrapper를 아래 단계로 실행하는 알고리즘으로 한다:
-
result를 pullAlgorithm이 주어졌으면 실행 결과로, 아니면 null로 한다. 만약 이 과정에서 예외 e가 발생하면 e로 reject된 promise를 반환한다.
-
result가
Promise
이면, result를 반환한다. -
undefined로 resolve된 promise를 반환한다.
-
-
cancelAlgorithmWrapper를 아래 단계로 실행하는 알고리즘으로 한다:
-
result를 cancelAlgorithm이 주어졌으면 실행 결과로, 아니면 null로 한다. 만약 이 과정에서 예외 e가 발생하면 e로 reject된 promise를 반환한다.
-
result가
Promise
이면, result를 반환한다. -
undefined로 resolve된 promise를 반환한다.
-
-
! InitializeReadableStream(stream)을 수행한다.
-
controller를 새로운
ReadableByteStreamController
로 한다. -
! SetUpReadableByteStreamController(stream, controller, startAlgorithm, pullAlgorithmWrapper, cancelAlgorithmWrapper, highWaterMark, undefined)를 수행한다.
ReadableStream
을
생성하려면 다음과 같이 두 단계로 진행합니다:
-
readableStream을 새로운
ReadableStream
으로 생성한다. -
설정(set up) readableStream을 … 조건에 맞게 수행한다.
ReadableStream
의 서브클래스는 생성자 단계에서 설정(set up) 또는
바이트 읽기 지원으로 설정(set up with byte reading
support)
연산을 this 값에 직접
사용합니다.
아래 알고리즘은 반드시 위의 설정(set up) 또는 바이트 읽기 지원으로 설정(set up with byte reading
support) 알고리즘으로 초기화된 ReadableStream
인스턴스에서만 사용해야 합니다 (예: 웹 개발자가 직접 생성한 인스턴스에서는 사용하지 않음):
ReadableStream
stream의 최대 수위까지 채워야 하는 원하는 크기(desired size to
fill up to the
high water mark)는 아래 단계를 따른 결과입니다:
-
stream이 읽기 가능(readable)이 아니면 0을 반환한다.
-
stream.[[controller]]가 구현하는 경우
ReadableByteStreamController
, !ReadableByteStreamControllerGetDesiredSize(stream.[[controller]])를 반환한다. -
그 외에는 ! ReadableStreamDefaultControllerGetDesiredSize(stream.[[controller]])를 반환한다.
ReadableStream
더 많은 데이터가 필요하다(needs more data)는 최대 수위까지 채워야 하는 원하는 크기가
0보다 클 때를 의미합니다.
ReadableStream
stream을 닫기(close) 위해서는:
-
stream.[[controller]]가 구현하는 경우
ReadableByteStreamController
,-
!ReadableByteStreamControllerClose(stream.[[controller]])를 수행한다.
-
stream.[[controller]].[[pendingPullIntos]] 가 비어 있지 않으면, ! ReadableByteStreamControllerRespond(stream.[[controller]], 0)를 수행한다.
-
-
그 외에는 !ReadableStreamDefaultControllerClose(stream.[[controller]])를 수행한다.
ReadableStream
stream에 자바스크립트 값 e로 에러(error)를 발생시키려면:
-
stream.[[controller]]가 구현하는 경우
ReadableByteStreamController
, !ReadableByteStreamControllerError(stream.[[controller]], e)를 수행한다. -
그 외에는 !ReadableStreamDefaultControllerError(stream.[[controller]], e)를 수행한다.
ReadableStream
stream에 enqueue하려면:
-
stream.[[controller]]가 구현하는 경우
ReadableStreamDefaultController
,-
!ReadableStreamDefaultControllerEnqueue(stream.[[controller]], chunk)를 수행한다.
-
-
그 외에는,
-
단언: stream.[[controller]]가 구현하는 경우
ReadableByteStreamController
. -
단언: chunk는
ArrayBufferView
이다. -
byobView를 stream의 현재 BYOB 요청 뷰(current BYOB request view)로 한다.
-
byobView가 null이 아니고, chunk.[[ViewedArrayBuffer]]가 byobView.[[ViewedArrayBuffer]]와 같으면:
-
단언: chunk.[[ByteOffset]]이 byobView.[[ByteOffset]]과 같다.
-
단언: chunk.[[ByteLength]] ≤ byobView.[[ByteLength]]이다.
이 단언은 호출자가 현재 BYOB 요청 뷰의 요청된 범위를 벗어나지 않도록 보장합니다.
-
? ReadableByteStreamControllerRespond(stream.[[controller]], chunk.[[ByteLength]])를 수행한다.
-
-
그 외에는 ? ReadableByteStreamControllerEnqueue(stream.[[controller]], chunk)를 수행한다.
-
아래 알고리즘은 반드시 위의 바이트 읽기 지원으로 설정(set up with byte reading
support) 알고리즘으로 초기화된 ReadableStream
인스턴스에서만 사용해야 합니다:
ReadableStream
stream의 현재 BYOB 요청 뷰(current BYOB request view)는 ArrayBufferView
또는 null이며, 아래 단계를 따라 결정됩니다:
-
단언: stream.[[controller]]가 구현하는 경우
ReadableByteStreamController
. -
byobRequest를 ! ReadableByteStreamControllerGetBYOBRequest(stream.[[controller]])로 한다.
-
byobRequest가 null이면 null을 반환한다.
-
byobRequest.[[view]]를 반환한다.
명세는 transfer하거나 detach해서는 안 됩니다. 기저 버퍼를 현재 BYOB 요청 뷰의 것으로 할 때 그렇습니다.
구현체는 예를 들어 다른 스레드에서 메모리에 쓰고 싶을 때 transfer와 유사한 동작을 할 수 있습니다. 하지만 그러려면 enqueue 및 close 알고리즘의 구현 방식을 약간 조정하여 동일한 관찰 가능한 결과를 유지해야 합니다. 명세상에서는 transfer와 detach가 그냥 금지됩니다.
명세는 가능하다면 write를 현재 BYOB 요청 뷰가 null이 아닌 경우 그 뷰에 실행하고, 그 뷰로 enqueue를
호출해야 합니다.
새로운 ArrayBufferView
를 생성하여 enqueue에 전달하는 것은
현재 BYOB 요청 뷰가 null일 때나,
보유한 바이트 수가 현재 BYOB 요청 뷰의
바이트 길이보다 많을 때만 해야 합니다. 이렇게 하면 불필요한 복사를 피할 수 있고
스트림의 소비자의 요구를 더 잘 존중할 수 있습니다.
다음 바이트로부터 풀 알고리즘은, 명세 수준에서 바이트 시퀀스를 기저 바이트 소스의 표현으로 사용하는 일반적인 경우에 이러한 요구사항을 구현합니다. 이 알고리즘은 보수적으로 동작하여 바이트를 바이트 시퀀스에 남겨두고, 공격적으로 enqueue하지 않습니다. 따라서 이 알고리즘의 호출자는 남아있는 바이트 수를 역압 신호로 활용할 수 있습니다.
ReadableStream
stream에 pull from bytes 하려면:
-
단언: stream.[[controller]]가 구현하는 경우
ReadableByteStreamController
. -
available을 bytes의 길이(length)로 한다.
-
desiredSize를 available로 한다.
-
stream의 현재 BYOB 요청 뷰가 null이 아니면 desiredSize를 stream의 현재 BYOB 요청 뷰의 byte length로 설정한다.
-
pullSize를 available과 desiredSize 중 더 작은 값으로 한다.
-
pulled를 bytes의 처음 pullSize 바이트로 한다.
-
bytes의 처음 pullSize 바이트를 제거한다.
-
stream의 현재 BYOB 요청 뷰가 null이 아니면:
-
write를 pulled를 stream의 현재 BYOB 요청 뷰에 대해 수행한다.
-
? ReadableByteStreamControllerRespond(stream.[[controller]], pullSize)를 수행한다.
-
-
그 외에는,
-
view를 생성한
Uint8Array
로 설정합니다. pulled로부터, stream의 관련 Realm에서 생성합니다. -
? ReadableByteStreamControllerEnqueue(stream.[[controller]], view)를 수행한다.
-
명세에서는 write를 현재 BYOB 요청 뷰나 pull from bytes를
닫기(close)한 ReadableStream
에
대해 수행해서는 안 됩니다.
9.1.2. 읽기
아래 알고리즘들은 웹 개발자가 생성한 인스턴스를 포함해 임의의 ReadableStream
인스턴스에서 사용할 수 있습니다. 모든 알고리즘은 각 작업별로 다양한 방식으로 실패할 수 있으며, 이러한 실패는 호출하는 명세에서 처리해야 합니다.
ReadableStream
stream의 리더(reader)를
가져오기(get a reader) 위해서는, ? AcquireReadableStreamDefaultReader(stream)를
반환합니다.
결과는 ReadableStreamDefaultReader
가
됩니다.
만약 stream이 이미 locked 상태라면 예외가 발생합니다.
ReadableStreamDefaultReader
reader에서 읽기 요청(read
request)
readRequest로 청크 읽기(read
a chunk)하려면, ! ReadableStreamDefaultReaderRead(reader,
readRequest)를 수행합니다.
ReadableStreamDefaultReader
reader에서 successSteps(바이트 시퀀스를 받는 알고리즘), failureSteps(자바스크립트 값을 받는
알고리즘)과 함께 모든 바이트 읽기(read all
bytes)하려면: read-loop를
reader, 새로운 바이트 시퀀스(byte sequence),
successSteps, failureSteps에 대해 실행합니다.
-
readRequest를 새로운 읽기 요청(read request)으로 하고, 다음 항목(items)을 포함합니다:
- chunk steps, chunk 전달받음
-
-
chunk가
Uint8Array
객체가 아니면, failureSteps에TypeError
를 전달하고 이 단계들을 중단합니다. -
chunk가 나타내는 바이트를 bytes에 추가합니다.
-
read-loop를 reader, bytes, successSteps, failureSteps에 대해 다시 실행합니다.
이 재귀는 직접 구현하면 스택 오버플로우가 발생할 수 있습니다. 구현에서는 비재귀로 바꾸거나, 마이크로태스크 큐잉(queue a microtask)를 사용하거나, 아래에 언급된 바와 같이 더 직접적인 바이트 읽기 메커니즘을 사용해야 합니다.
-
- close steps
-
-
successSteps를 bytes에 대해 호출합니다.
-
- error steps, e 전달받음
-
-
failureSteps를 e에 대해 호출합니다.
-
-
! ReadableStreamDefaultReaderRead(reader, readRequest)를 수행합니다.
reader는 해당 ReadableStream
에
배타적 접근 권한을 주기 때문에, 실제 읽기 메커니즘은 관찰할 수 없습니다. 구현체는 더 직접적인 메커니즘(예: ReadableStreamBYOBReader
대신 ReadableStreamDefaultReader
를
사용하거나, 청크를 직접 접근 등)을 사용할 수 있습니다.
ReadableStreamDefaultReader
reader를 해제(release)하려면,
!ReadableStreamDefaultReaderRelease(reader)를
수행합니다.
ReadableStreamDefaultReader
reader를 reason과 함께 취소(cancel)하려면,
!ReadableStreamReaderGenericCancel(reader,
reason)를 수행합니다. 반환값은 undefined로 fulfill되거나 실패 이유로 reject되는 promise입니다.
ReadableStream
stream을 reason과 함께 취소(cancel)하려면,
!ReadableStreamCancel(stream, reason)를
반환합니다. 반환값은 undefined로 fulfill되거나 실패 이유로 reject되는 promise입니다.
ReadableStream
stream을 tee(분기)하려면,
? ReadableStreamTee(stream, true)를 반환합니다.
ReadableStreamTee의 두 번째 인자로 true를 전달하면, 반환된 두 번째 분기의 청크가 첫 번째 분기에서 복제(clone)됩니다(HTML의 serializable objects 프레임워크 사용). 한 분기에서 데이터를 소비해도 다른 분기에는 영향이 가지 않습니다.
9.1.3. 내부 상태 점검(Introspection)
아래의 판별자(predicates)는 임의의 ReadableStream
객체에서 사용할 수 있습니다. 단, 스트림이 locked 상태인지 확인하는 것 이외에는, 이러한 직접적인 내부 상태 점검은 공개 자바스크립트 API로
불가능하므로, 명세에서는 § 9.1.2 읽기에 있는 알고리즘을 사용해야 합니다. (예: 스트림이 읽기
가능(readable)한지 테스트하는 대신 리더를 가져오기(get a reader)를 시도하고 예외를 처리하기.)
ReadableStream
stream이 읽기 가능(readable)하려면
stream.[[state]]가 "readable
"일 때입니다.
ReadableStream
stream이 닫힘(closed) 상태이려면,
stream.[[state]]가 "closed
"일 때입니다.
ReadableStream
stream이 오류 상태(errored)이려면,
stream.[[state]]가 "errored
"일 때입니다.
ReadableStream
stream이 locked(잠김) 상태이려면 ! IsReadableStreamLocked(stream)이 true를 반환해야 합니다.
ReadableStream
stream이 disturbed(교란됨) 상태이려면 stream.[[disturbed]]가
true여야 합니다.
이 값은 스트림이 한 번이라도 읽히거나 취소되었는지 여부를 나타냅니다. 이 섹션의 다른 판별자들보다도 더욱, 이 값은 명세에서 신중하게 사용해야 하며, 웹 개발자가 간접적으로라도 접근할 수 없는 정보이므로 플랫폼 동작을 이 값에 따라 분기하는 것은 바람직하지 않습니다.
9.2. 쓰기 스트림(Writable streams)
9.2.1. 생성과 조작
WritableStream
객체 stream을 설정(set up)하려면, 알고리즘 writeAlgorithm, 선택적
알고리즘 closeAlgorithm, 선택적
알고리즘 abortAlgorithm, 선택적 숫자
highWaterMark (기본값 1),
선택적 알고리즘 sizeAlgorithm을 받아 아래 단계를 수행합니다.
writeAlgorithm은 chunk 객체를 받아
promise를 반환하는 알고리즘이어야 합니다. closeAlgorithm과 abortAlgorithm이 주어지면 promise를 반환할 수 있습니다.
sizeAlgorithm이 주어지면 반드시 chunk 객체를
받아 숫자를 반환하는 알고리즘이어야 하고, highWaterMark가 주어지면 반드시 음수가 아니고 NaN이 아닌 숫자이어야 합니다.
-
startAlgorithm을 undefined를 반환하는 알고리즘으로 한다.
-
closeAlgorithmWrapper를 아래 단계로 실행하는 알고리즘으로 한다:
-
result를 closeAlgorithm이 주어졌으면 실행 결과로, 아니면 null로 한다. 만약 이 과정에서 예외 e가 발생하면 e로 reject된 promise를 반환한다.
-
result가
Promise
이면, result를 반환한다. -
undefined로 resolve된 promise를 반환한다.
-
-
abortAlgorithmWrapper를 reason을 받아 아래 단계로 실행하는 알고리즘으로 한다:
-
result를 abortAlgorithm이 주어졌다면 reason을 전달하여 실행한 결과로, 아니면 null로 한다. 이 과정에서 예외 e가 발생하면 e로 reject된 promise를 반환한다.
-
result가
Promise
이면, result를 반환한다. -
undefined로 resolve된 promise를 반환한다.
-
-
sizeAlgorithm이 주어지지 않았다면 1을 반환하는 알고리즘으로 설정한다.
-
! InitializeWritableStream(stream)을 수행한다.
-
controller를 새로운
WritableStreamDefaultController
로 한다. -
! SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithmWrapper, abortAlgorithmWrapper, highWaterMark, sizeAlgorithm)를 수행한다.
다른 명세에서
writeAlgorithm을 작성할 때는, 주어진 chunk에서 병렬(parallel)로 읽기를 하지 않도록 주의해야
합니다. 병렬 읽기는 자바스크립트의 실행완료(run-to-completion) 의미론을 위반할 수 있습니다. 이를 피하려면 StructuredSerializeWithTransfer나, 버퍼 소스의 바이트 복사본(get a copy of the bytes held by the buffer
source)을 만들거나, ArrayBuffer를 전송(transferring)하는 등의 동기적 복사나 전송을 하면 됩니다. 단, chunk가 SharedArrayBuffer
인
경우에는 병렬 변경이 불가피합니다.
WritableStream
을
생성하려면 다음과 같이 두 단계로 진행합니다:
-
writableStream을 새로운
WritableStream
으로 생성한다. -
설정(set up) writableStream을 … 조건에 맞게 수행한다.
WritableStream
서브클래스는 생성자 단계에서 설정(set up) 연산을 this 값에서 직접 사용합니다.
아래 정의들은 반드시 위의 설정(set up) 알고리즘으로 초기화된 WritableStream
인스턴스에서만 사용해야 합니다:
WritableStream
stream에 자바스크립트 값 e로 에러(error)를
발생시키려면
!WritableStreamDefaultControllerErrorIfNeeded(stream.[[controller]], e)를 수행합니다.
WritableStream
stream의 signal은
stream.[[controller]].[[abortController]]의
signal입니다. 명세에서는 이 AbortSignal
에
알고리즘 추가(add)나 알고리즘
제거(remove)를 하거나,
abort 상태(aborted) 및
abort 이유(abort reason)를 확인할 수 있습니다.
일반적인 사용 방식은, WritableStream
을
설정(set up)한 뒤,
알고리즘을 signal에 추가(add)해서 underlying sink에서 진행 중인 쓰기 작업을 중단(abort)합니다.
그리고 writeAlgorithm 내부에서는 underlying sink가 응답한 뒤
signal이 aborted 상태라면, signal의 abort 이유(abort reason)로 반환된 promise를 reject해야 합니다.
9.2.2. 쓰기
아래 알고리즘들은 웹 개발자가 생성한 인스턴스를 포함해 임의의 WritableStream
인스턴스에서 사용할 수 있습니다. 모든 알고리즘은 각 작업별로 다양한 방식으로 실패할 수 있으며, 이러한 실패는 호출하는 명세에서 처리해야 합니다.
WritableStream
stream의 writer를
가져오기(get a writer) 위해서는, ? AcquireWritableStreamDefaultWriter(stream)를
반환합니다.
결과는 WritableStreamDefaultWriter
가
됩니다.
만약 stream이 이미 잠겨 있으면 예외가 발생합니다.
WritableStreamDefaultWriter
writer에 chunk 값을 쓰기(write a chunk)하려면, ! WritableStreamDefaultWriterWrite(writer,
chunk)를 반환합니다.
WritableStreamDefaultWriter
writer를 해제(release)하려면,
!WritableStreamDefaultWriterRelease(writer)를
수행합니다.
WritableStream
stream을 닫기(close)하려면,
!WritableStreamClose(stream)를 반환합니다. 반환값은 undefined로
fulfill되거나 실패 이유로 reject되는 promise입니다.
WritableStream
stream을 reason과 함께 중단(abort)하려면,
!WritableStreamAbort(stream, reason)를
반환합니다. 반환값은 undefined로 fulfill되거나 실패 이유로 reject되는 promise입니다.
9.3. 변환 스트림(Transform streams)
9.3.1. 생성과 조작
TransformStream
객체 stream을 설정(set up)하려면, 알고리즘 transformAlgorithm, 선택적 알고리즘 flushAlgorithm, 선택적 알고리즘
cancelAlgorithm을 받아 아래 단계를 수행합니다.
transformAlgorithm과, 주어졌다면 flushAlgorithm 및 cancelAlgorithm도 promise를 반환할 수
있습니다.
-
writableHighWaterMark를 1로 한다.
-
writableSizeAlgorithm을 1을 반환하는 알고리즘으로 한다.
-
readableHighWaterMark를 0으로 한다.
-
readableSizeAlgorithm을 1을 반환하는 알고리즘으로 한다.
-
transformAlgorithmWrapper를 아래 단계로 chunk 값을 받아 실행하는 알고리즘으로 한다:
-
result를 transformAlgorithm에 chunk를 전달해 실행한 결과로 한다. 예외 e가 발생하면 e로 reject된 promise를 반환한다.
-
result가
Promise
이면, result를 반환한다. -
undefined로 resolve된 promise를 반환한다.
-
-
flushAlgorithmWrapper를 아래 단계로 실행하는 알고리즘으로 한다:
-
result를 flushAlgorithm이 주어졌으면 실행 결과로, 아니면 null로 한다. 예외 e가 발생하면 e로 reject된 promise를 반환한다.
-
result가
Promise
이면, result를 반환한다. -
undefined로 resolve된 promise를 반환한다.
-
-
cancelAlgorithmWrapper를 reason을 받아 아래 단계로 실행하는 알고리즘으로 한다:
-
result를 cancelAlgorithm이 주어졌다면 reason을 전달해 실행한 결과로, 아니면 null로 한다. 예외 e가 발생하면 e로 reject된 promise를 반환한다.
-
result가
Promise
이면, result를 반환한다. -
undefined로 resolve된 promise를 반환한다.
-
-
startPromise를 undefined로 resolve된 promise로 한다.
-
! InitializeTransformStream(stream, startPromise, writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark, readableSizeAlgorithm)를 수행한다.
-
controller를 새로운
TransformStreamDefaultController
로 한다. -
! SetUpTransformStreamDefaultController(stream, controller, transformAlgorithmWrapper, flushAlgorithmWrapper, cancelAlgorithmWrapper)를 수행한다.
다른 명세에서
transformAlgorithm을 작성할 때는, 주어진
chunk에서 병렬(parallel)로 읽기를 하지 않도록 주의해야
합니다. 병렬 읽기는 자바스크립트의 실행완료(run-to-completion) 의미론을 위반할 수 있습니다. 이를 피하려면 StructuredSerializeWithTransfer나, 버퍼 소스의 바이트 복사본(get a copy of the bytes held by the buffer
source)을 만들거나, ArrayBuffer를 전송(transferring)하는 등의 동기적 복사나 전송을 하면 됩니다. 단, chunk가 SharedArrayBuffer
인
경우에는 병렬 변경이 불가피합니다.
TransformStream
을
생성하려면 다음과 같이 두 단계로 진행합니다:
-
transformStream을 새로운
TransformStream
으로 생성한다. -
설정(set up) transformStream을 … 조건에 맞게 수행한다.
TransformStream
서브클래스는 생성자 단계에서 설정(set up) 연산을 this 값에서 직접 사용합니다.
TransformStream
생성(create an identity TransformStream)하려면:
-
transformStream을 새로운
TransformStream
으로 생성한다. -
설정(set up) transformStream을 transformAlgorithm을 chunk를 받아 enqueue하는 알고리즘으로 설정하여 수행한다.
-
transformStream을 반환한다.
아래 알고리즘들은 반드시 위의 설정(set up) 알고리즘으로 초기화된 TransformStream
인스턴스에서만 사용해야 합니다. 보통 transformAlgorithm 또는 flushAlgorithm에서 호출합니다.
TransformStream
stream에 자바스크립트 값 chunk를 enqueue하려면,
!TransformStreamDefaultControllerEnqueue(stream.[[controller]], chunk)를 수행합니다.
TransformStream
stream을 종료(terminate)하려면,
!TransformStreamDefaultControllerTerminate(stream.[[controller]])를 수행합니다.
TransformStream
stream에 자바스크립트 값 e로 에러(error)를 발생시키려면,
!TransformStreamDefaultControllerError(stream.[[controller]], e)를 수행합니다.
9.3.2. 커스텀 클래스로 래핑(Wrapping into a custom class)
커스텀 변환 스트림(transform
streams)을 정의하려는 다른 명세들은 TransformStream
인터페이스를 직접 상속하지 않을 수도 있습니다. 새로운 클래스가 필요하다면, 독립적인 Web IDL 인터페이스를 만들고 다음 믹스인(mixin)을 사용할 수 있습니다:
interface mixin {
GenericTransformStream readonly attribute ReadableStream readable ;readonly attribute WritableStream writable ; };
플랫폼 객체(platform object)가 GenericTransformStream
믹스인을 include하면,
해당 객체에는 실제 TransformStream
인
transform이 연관됩니다.
readable
getter 단계는
this의
transform.[[readable]]을 반환합니다.
writable
getter 단계는
this의
transform.[[writable]]을 반환합니다.
GenericTransformStream
믹스인을 포함하면 IDL 인터페이스에 readable
및 writable
프로퍼티가 추가됩니다. 결과 인터페이스의 동작을 커스터마이즈하려면, 생성자(또는 다른 초기화 코드)에서 각 인스턴스의 transform을
새로운 TransformStream
으로
설정한 뒤,
설정(set up)을
transformAlgorithm 및 선택적으로
flushAlgorithm 인자를 통해 진행하면 됩니다.
참고: 이 패턴의 기존 사례로는 CompressionStream
및
TextDecoderStream
등이 있습니다.
[COMPRESSION] [ENCODING]
기본 TransformStream
클래스가 제공하는 API 외에 별도의 API가 필요 없다면 래퍼 클래스를 만들 필요가 없습니다. 가장 흔한 래퍼 필요 사례는 커스텀 생성자
동작(constructor steps)이지만, 만약 개념적 변환 스트림이 생성될 필요가 없다면 TransformStream
을
직접 사용하는 것이 문제없습니다.
9.4. 기타 스트림 쌍(Other stream pairs)
위에서 다룬 변환 스트림(transform streams) 이외에도, 명세에서는 읽기 스트림(readable)과 쓰기 스트림(writable)의 쌍을 자주 만듭니다. 이 섹션에서는 그러한 상황에 대한 가이드를 제공합니다.
이러한 모든 경우에, 명세에서는 스트림을 노출하는 두 프로퍼티 이름으로 readable
및 writable
을 사용해야 합니다. 다른 이름(예:
input
/output
또는 readableStream
/writableStream
)을 사용하거나,
스트림 접근을 위한 메서드/비프로퍼티 접근을 사용하면 안 됩니다.
9.4.1. 이중 스트림(Duplex streams)
가장 일반적인 읽기/쓰기 쌍은 이중 스트림(duplex stream)으로, 읽기와 쓰기 스트림이 하나의 공유 리소스(예: 소켓, 연결, 장치)의 양쪽을 나타냅니다.
이중 스트림을 명세할 때 가장 까다로운 점은 읽기 측을 취소(canceling)하거나 쓰기 측을 닫거나 중단(aborting)하는 등 양쪽의 동작을 어떻게 처리할지입니다. 한쪽의 동작이 다른 쪽에 영향을 주지 않게 "half open"으로 둘 수도 있고, 반대로 효과를 다른 쪽에 전파하는 것이 더 나을 수도 있습니다. 예를 들어, 읽기 측의 cancelAlgorithm이 쓰기 측을 닫도록(close) 명세할 수 있습니다.
기본적인 이중 스트림 예시는 명세 텍스트 대신 JavaScript로 생성되며, § 10.8 동일 리소스를 래핑하는 { readable, writable } 스트림 쌍에서 볼 수 있습니다. 이 예시는 효과 전파 동작을 보여줍니다.
또 다른 고려 사항은 비동기적으로 획득해야 하는 이중 스트림(예: 연결 수립)을 어떻게 생성할지입니다. 권장 패턴은 promise를 반환하는 프로퍼티를 가진 생성 가능한 클래스로, 해당 프로퍼티가 실제 이중 스트림 객체로 fulfill됩니다. 이 이중 스트림 객체는 비동기로만 얻을 수 있는 정보(예: 연결 데이터)도 노출할 수 있습니다. 컨테이너 클래스는 연결 전체를 닫는 함수 등 편의 API도 제공할 수 있습니다.
이런 복잡한 이중 스트림의 예는 아직 명세 중인 WebSocketStream
입니다.
자세한 내용은 explainer와 디자인
노트(design notes)를 참고하세요.
이중 스트림은 readable
/writable
프로퍼티 규약을 따르므로 pipeThrough()
와
함께 사용할 수 있습니다. 항상 의미가 있는 것은 아니지만, 실제로 리소스가 변환 역할을 한다면 유용할 수 있습니다.
임의의 WebSocket에서 파이프스루(pipeThrough)로 이중 스트림을 사용하는 것은 의미가 없습니다. 하지만 WebSocket 서버가 들어온 메시지에 대해 변환된 데이터를 다시 보내는 식으로 동작한다면, 매우 편리할 수 있습니다.
9.4.2. 엔드포인트 쌍(Endpoint pairs)
읽기/쓰기 쌍의 또 다른 유형은 엔드포인트 쌍(endpoint pair)입니다. 이 경우 읽기/쓰기 스트림은 더 긴 파이프라인의 양쪽 끝을 나타내며, 웹 개발자 코드가 중앙에 변환 스트림(transform streams)을 삽입하는 것이 목적입니다.
createEndpointPair()
함수를 제공한다면, 웹 개발자는 아래와 같이 코드를 작성할 수 있습니다:
const { readable, writable} = createEndpointPair(); await readable. pipeThrough( new TransformStream(...)). pipeTo( writable);
WebRTC Encoded Transform
은 이 기술의 예시로, RTCRtpScriptTransformer
인터페이스에 readable
과 writable
속성이 모두 있습니다.
이러한 엔드포인트 쌍도 readable
/writable
프로퍼티 규약을 따르지만, pipeThrough()
에
전달하는 것은 의미가 없습니다.
9.5. 파이핑(Piping)
ReadableStream
readable을 WritableStream
writable에 파이프(pipe to)할 때의 결과는, 선택적 불린 값 preventClose
(기본값 false), 선택적 불린 값 preventAbort (기본값
false), 선택적 불린 값 preventCancel
(기본값
false), 선택적 AbortSignal
signal을 받아 아래 단계를 수행하여 얻을 수 있습니다.
반환값은 파이핑이 완료되면 fulfill, 실패하면 예외로 reject되는 Promise
입니다.
-
단언: ! IsReadableStreamLocked(readable)가 false이다.
-
단언: ! IsWritableStreamLocked(writable)가 false이다.
-
signalArg를 signal이 주어졌으면 signal로, 아니면 undefined로 한다.
-
! ReadableStreamPipeTo(readable, writable, preventClose, preventAbort, preventCancel, signalArg)를 반환한다.
반환된 promise에 신경 쓰지 않는다면, 이 개념을 참조하기가 다소 애매할 수 있습니다. 가장 좋은 표현은 "readable을 writable에 pipe한다"입니다.
ReadableStream
readable을 TransformStream
transform에 파이프스루(pipe through)할 때의 결과는 선택적 불린 값 preventClose
(기본값 false), 선택적 불린 값 preventAbort
(기본값 false), 선택적 불린 값 preventCancel (기본값 false), 선택적 AbortSignal
signal을 받아 아래 단계를
수행합니다. 결과는 transform의 읽기 측(readable side)입니다.
-
단언: ! IsReadableStreamLocked(readable)가 false이다.
-
단언: ! IsWritableStreamLocked(transform.[[writable]])가 false이다.
-
signalArg를 signal이 주어졌으면 signal로, 아니면 undefined로 한다.
-
promise를 ! ReadableStreamPipeTo(readable, transform.[[writable]], preventClose, preventAbort, preventCancel, signalArg)로 한다.
-
promise.[[PromiseIsHandled]]를 true로 설정한다.
-
transform.[[readable]]을 반환한다.
ReadableStream
stream의 프록시(proxy) 생성은 다음 단계를
수행합니다. 결과는 stream의 데이터를 풀링(pull)하는 새 ReadableStream
객체이며, stream 자체는 즉시 잠김(locked) 및 교란(disturbed) 상태가
됩니다.
-
identityTransform을 identity TransformStream 생성(create an identity
TransformStream
)의 결과로 한다. -
stream을 identityTransform에 파이프스루(pipe through)한 결과를 반환한다.
10. 스트림 생성 예시
이 섹션 및 모든 하위 섹션은 비규범적입니다.
이전에 현행 표준 전반에서 예시는 스트림을 어떻게 사용하는지에 중점을 두었습니다. 여기서는 ReadableStream
,
WritableStream
,
TransformStream
생성자를 사용하여 스트림을 생성하는 방법을 보여줍니다.
10.1. 기본 푸시 소스(역압력 미지원)로 읽기 가능한 스트림
다음 함수는 읽기 가능한 스트림을
생성하며, WebSocket
인스턴스를 감쌉니다. [WEBSOCKETS],
이는 푸시 소스이지만 역압력 신호를 지원하지 않습니다.
푸시 소스를 어댑트할 때 대부분의 작업이 start()
메서드에서 이루어지는 것을 보여줍니다.
function makeReadableWebSocketStream( url, protocols) { const ws= new WebSocket( url, protocols); ws. binaryType= "arraybuffer" ; return new ReadableStream({ start( controller) { ws. onmessage= event=> controller. enqueue( event. data); ws. onclose= () => controller. close(); ws. onerror= () => controller. error( new Error ( "The WebSocket errored!" )); }, cancel() { ws. close(); } }); }
이 함수를 사용하여 웹 소켓용 읽기 가능한 스트림을 생성하고, 해당 스트림을 임의의 쓰기 가능한 스트림에 파이프할 수 있습니다:
const webSocketStream= makeReadableWebSocketStream( "wss://example.com:443/" , "protocol" ); webSocketStream. pipeTo( writableStream) . then(() => console. log( "모든 데이터가 성공적으로 기록되었습니다!" )) . catch ( e=> console. error( "문제가 발생했습니다!" , e));
하지만 많은 사람들이 "웹 소켓에 스트림 지원을 추가한다"고 말할 때, 실제로는 메시지 하나를 스트리밍 방식으로 전송할 수 있는 새로운 기능을 기대하는 경우가 많습니다. 예를 들어,
파일을 하나의 메시지로 전송하면서 클라이언트 측에서 전체 내용을 메모리에 저장하지 않아도 되는 기능입니다. 이를 위해서는 개별 웹 소켓 메시지 자체가 ReadableStream
인스턴스가 되도록 해야 합니다. 위 예시에서는 이 부분을 다루지 않습니다.
배경 설명은 이 토론을 참고하세요.
10.2. 기본 푸시 소스와 역압력 지원이 있는 읽기 가능한 스트림
다음 함수는 "역압력 소켓"을 감싸는 읽기
가능한 스트림을 반환합니다. 이 소켓은 웹 소켓과 동일한 API를 가지지만 readStop
과 readStart
메서드를 통해
데이터 흐름을 일시 중지 및 재개할 수 있습니다. 이 예시는 역압력을 지원하는 기본 소스에 어떻게 적용하는지 보여줍니다.
function makeReadableBackpressureSocketStream( host, port) { const socket= createBackpressureSocket( host, port); return new ReadableStream({ start( controller) { socket. ondata= event=> { controller. enqueue( event. data); if ( controller. desiredSize<= 0 ) { // 내부 큐가 가득 찼으므로, // 역압력 신호를 기본 소스에 전달합니다. socket. readStop(); } }; socket. onend= () => controller. close(); socket. onerror= () => controller. error( new Error ( "The socket errored!" )); }, pull() { // 내부 큐가 비어 있지만, 스트림 소비자가 더 많은 데이터를 원할 때 호출됩니다. // 이전에 일시 중지했으면 데이터 흐름을 다시 시작합니다. socket. readStart(); }, cancel() { socket. close(); } }); }
이 함수를 사용하여 "역압력 소켓"에 대해 읽기 가능한 스트림을 생성할 수 있습니다. 이번에는 목적지가 데이터를 소켓만큼 빨리 받아들이지 못하거나, 스트림을 오랫동안 읽지 않으면, 역압력 신호가 소켓으로 전달됩니다.
10.3. 기본 푸시 소스(역압력 미지원)로 읽기 가능한 바이트 스트림
다음 함수는 가상의 UDP 소켓 API를 감싸는 읽기 가능한 바이트 스트림을 반환합니다. select2()
메서드는 POSIX의
select(2) 시스템 호출을 연상시키는 Promise 반환 방식입니다.
UDP 프로토콜에는 내장된 역압력 지원이 없으므로, desiredSize
신호는 무시되며, 소켓에서 데이터를 받을 수 있지만 아직 개발자가 요청하지 않은 경우에는 스트림의 내부 큐에 데이터를 넣어 커널 공간 큐가 넘치는 것을 방지합니다.
이로 인해 소비자가 스트림과 상호작용하는 방식에 흥미로운 결과가 나타납니다. 소비자가 소켓보다 느리게 읽으면 청크가 스트림의 내부 큐에 무한히 남아 있을 수 있습니다. 이 경우 BYOB 리더를 사용하면 스트림 내부 큐에서 개발자가 제공한 버퍼로 데이터를 복사해야 하므로 추가 복사가 발생합니다. 하지만 소비자가 데이터를 충분히 빨리 읽으면 BYOB 리더를 통해 버퍼에 직접(제로 복사로) 읽을 수 있습니다.
(좀 더 복잡한 예시에서는 desiredSize
값을 이용해 소켓에 메시지를 보내 데이터 전송 속도를 조절하는 등, 역압력 신호를 별도의 채널로 전달할 수도 있습니다. 이는 독자의 연습 과제로 남깁니다.)
const DEFAULT_CHUNK_SIZE= 65536 ; function makeUDPSocketStream( host, port) { const socket= createUDPSocket( host, port); return new ReadableStream({ type: "bytes" , start( controller) { readRepeatedly(). catch ( e=> controller. error( e)); function readRepeatedly() { return socket. select2(). then(() => { // 소켓이 BYOB 요청 없이 읽을 수 있게 되는 경우도 있으므로 둘 다 처리해야 합니다. let bytesRead; if ( controller. byobRequest) { const v= controller. byobRequest. view; bytesRead= socket. readInto( v. buffer, v. byteOffset, v. byteLength); if ( bytesRead=== 0 ) { controller. close(); } controller. byobRequest. respond( bytesRead); } else { const buffer= new ArrayBuffer( DEFAULT_CHUNK_SIZE); bytesRead= socket. readInto( buffer, 0 , DEFAULT_CHUNK_SIZE); if ( bytesRead=== 0 ) { controller. close(); } else { controller. enqueue( new Uint8Array( buffer, 0 , bytesRead)); } } if ( bytesRead=== 0 ) { return ; } return readRepeatedly(); }); } }, cancel() { socket. close(); } }); }
ReadableStream
인스턴스는 이제 BYOB 리더를 제공할 수 있으며,
앞서 설명한 장점과 주의점을 모두 가집니다.
10.4. 기본 풀 소스를 사용하는 읽기 가능한 스트림
다음 함수는 읽기 가능한 스트림을
반환하며, Node.js 파일 시스템 API(C의 fopen
,
fread
, fclose
와 거의 동일)의 일부를 감쌉니다. 파일은 풀 소스의 전형적인 예입니다. 푸시 소스 예시와 달리, 대부분의 작업이 pull()
함수에서 수요에 따라 실행되며, start()
함수에서는 실행되지 않습니다.
const fs= require( "fs" ). promises; const CHUNK_SIZE= 1024 ; function makeReadableFileStream( filename) { let fileHandle; let position= 0 ; return new ReadableStream({ async start() { fileHandle= await fs. open( filename, "r" ); }, async pull( controller) { const buffer= new Uint8Array( CHUNK_SIZE); const { bytesRead} = await fileHandle. read( buffer, 0 , CHUNK_SIZE, position); if ( bytesRead=== 0 ) { await fileHandle. close(); controller. close(); } else { position+= bytesRead; controller. enqueue( buffer. subarray( 0 , bytesRead)); } }, cancel() { return fileHandle. close(); } }); }
이제 파일에 대해 읽기 가능한 스트림을 생성하고, 소켓과 마찬가지로 사용할 수 있습니다.
10.5. 기본 풀 소스를 사용하는 읽기 가능한 바이트 스트림
다음 함수는 읽기 가능한 바이트 스트림을 반환하며, 효율적인 제로 복사 파일 읽기를 지원합니다. 역시 Node.js 파일 시스템 API를 사용합니다. 1024의 정해진 청크 크기 대신 개발자가 제공한 버퍼를 최대한 채웁니다.
const fs= require( "fs" ). promises; const DEFAULT_CHUNK_SIZE= 1024 ; function makeReadableByteFileStream( filename) { let fileHandle; let position= 0 ; return new ReadableStream({ type: "bytes" , async start() { fileHandle= await fs. open( filename, "r" ); }, async pull( controller) { // 소비자가 기본 리더를 사용하더라도 auto-allocation이 버퍼를 할당해 byobRequest로 전달함 const v= controller. byobRequest. view; const { bytesRead} = await fileHandle. read( v, 0 , v. byteLength, position); if ( bytesRead=== 0 ) { await fileHandle. close(); controller. close(); controller. byobRequest. respond( 0 ); } else { position+= bytesRead; controller. byobRequest. respond( bytesRead); } }, cancel() { return fileHandle. close(); }, autoAllocateChunkSize: DEFAULT_CHUNK_SIZE}); }
이제 반환된 ReadableStream
에 대해 BYOB 리더를 생성하거나, 기본 리더를 생성해 통상적인 방식으로
사용할 수 있습니다.
여기서 보여준 기본
바이트 소스의 저수준 바이트 처리와 기본 리더의 고수준 청크 기반 소비 간의 어댑테이션은 스트림 구현에서 자동으로 처리됩니다. autoAllocateChunkSize
옵션 덕분에 § 10.3 기본 푸시 소스(역압력 미지원)로 읽기 가능한 바이트 스트림에서처럼 분기 코드를 줄일 수 있습니다.
10.6. 역압력 또는 성공 신호 없는 쓰기 가능한 스트림
다음 함수는 쓰기 가능한 스트림을
반환하며, WebSocket
[WEBSOCKETS]을 감쌉니다.
웹 소켓은 특정 데이터 청크가 성공적으로 전송되었는지 알 수 있는 방법을 제공하지 않으며(bufferedAmount
를
반복적으로 확인하는 불편한 방법은 독자의 연습 과제로 남깁니다), 이 쓰기 스트림은 역압력
신호나 쓰기 성공/실패 정보를 생산자에게 정확하게 전달할 수
없습니다. 즉, writer의 write()
메서드와
ready
접근자의 Promise는 항상 즉시 완료됩니다.
function makeWritableWebSocketStream( url, protocols) { const ws= new WebSocket( url, protocols); return new WritableStream({ start( controller) { ws. onerror= () => { controller. error( new Error ( "The WebSocket errored!" )); ws. onclose= null ; }; ws. onclose= () => controller. error( new Error ( "The server closed the connection unexpectedly!" )); return new Promise( resolve=> ws. onopen= resolve); }, write( chunk) { ws. send( chunk); // 웹 소켓이 쓰기 완료 시점을 알려주지 않으므로 즉시 반환합니다. }, close() { return closeWS( 1000 ); }, abort( reason) { return closeWS( 4000 , reason&& reason. message); }, }); function closeWS( code, reasonString) { return new Promise(( resolve, reject) => { ws. onclose= e=> { if ( e. wasClean) { resolve(); } else { reject( new Error ( "The connection was not closed cleanly" )); } }; ws. close( code, reasonString); }); } }
이 함수를 사용하여 웹 소켓용 쓰기 가능한 스트림을 생성하고, 임의의 읽기 가능한 스트림을 해당 웹 소켓에 파이프할 수 있습니다:
const webSocketStream= makeWritableWebSocketStream( "wss://example.com:443/" , "protocol" ); readableStream. pipeTo( webSocketStream) . then(() => console. log( "모든 데이터가 성공적으로 기록되었습니다!" )) . catch ( e=> console. error( "문제가 발생했습니다!" , e));
앞서 언급한 노트에서 웹 소켓을 스트림으로 래핑하는 방식에 대해 참고하세요.
10.7. 역압력 및 성공 신호가 있는 쓰기 가능한 스트림
다음 함수는 쓰기 가능한 스트림을
반환하며, Node.js 파일 시스템 API(C의 fopen
,
fwrite
, fclose
와 거의 동일)의 일부를 감쌉니다. 감싸는 API가 쓰기가 성공했는지 알려주므로, 이 스트림은 역압력 신호와 개별 쓰기 성공/실패 정보를
전달할 수 있습니다.
const fs= require( "fs" ). promises; function makeWritableFileStream( filename) { let fileHandle; return new WritableStream({ async start() { fileHandle= await fs. open( filename, "w" ); }, write( chunk) { return fileHandle. write( chunk, 0 , chunk. length); }, close() { return fileHandle. close(); }, abort() { return fileHandle. close(); } }); }
이 함수를 사용하여 파일용 쓰기 가능한 스트림을 생성하고, 개별 청크 데이터를 쓸 수 있습니다:
const fileStream= makeWritableFileStream( "/example/path/on/fs.txt" ); const writer= fileStream. getWriter(); writer. write( "To stream, or not to stream\n" ); writer. write( "That is the question\n" ); writer. close() . then(() => console. log( "청크가 기록되고 스트림이 성공적으로 종료되었습니다!" )) . catch ( e=> console. error( e));
fileHandle.write
호출이 오래 걸릴 경우, 반환된 Promise는 더 늦게 완료됩니다. 그동안 추가 쓰기는 대기열에 쌓이며, 이 대기열의 청크가 많아지면
ready
접근자는 보류 중인 Promise를 반환하게 됩니다. 이는 생산자에게 쓰기를 잠시 멈추는 것이 좋다는 신호입니다.
쓰기 가능한 스트림의 대기열 관리 방식은 특히 중요합니다. fileHandle.write
문서에 따르면, "동일한 파일에 대해 여러 번 filehandle.write를 사용하면서 Promise를 기다리지 않으면 안전하지 않습니다." 하지만
makeWritableFileStream
함수에서는 스트림 구현이 기본 sink의 write()
가
이전 Promise가 완료될 때까지 호출되지 않도록 보장하므로 걱정하지 않아도 됩니다!
10.8. 동일한 리소스를 감싸는 { 읽기, 쓰기 } 스트림 쌍
다음 함수는 { readable, writable }
형태의 객체를 반환하며, readable
속성에는 읽기 가능한 스트림,
writable
속성에는 쓰기 가능한 스트림이 들어 있습니다. 두 스트림 모두 동일한 웹 소켓 리소스를 감쌉니다. 즉, § 10.1 기본 푸시 소스(역압력 미지원)로 읽기 가능한 스트림과 § 10.6 역압력 또는 성공 신호 없는 쓰기 가능한 스트림을 결합한 것입니다.
또한, 자바스크립트 클래스를 활용해 재사용 가능한 기본 sink와 소스 추상화를 만드는 방법도 보여줍니다.
function streamifyWebSocket( url, protocol) { const ws= new WebSocket( url, protocols); ws. binaryType= "arraybuffer" ; return { readable: new ReadableStream( new WebSocketSource( ws)), writable: new WritableStream( new WebSocketSink( ws)) }; } class WebSocketSource{ constructor ( ws) { this . _ws= ws; } start( controller) { this . _ws. onmessage= event=> controller. enqueue( event. data); this . _ws. onclose= () => controller. close(); this . _ws. addEventListener( "error" , () => { controller. error( new Error ( "The WebSocket errored!" )); }); } cancel() { this . _ws. close(); } } class WebSocketSink{ constructor ( ws) { this . _ws= ws; } start( controller) { this . _ws. onclose= () => controller. error( new Error ( "The server closed the connection unexpectedly!" )); this . _ws. addEventListener( "error" , () => { controller. error( new Error ( "The WebSocket errored!" )); this . _ws. onclose= null ; }); return new Promise( resolve=> this . _ws. onopen= resolve); } write( chunk) { this . _ws. send( chunk); } close() { return this . _closeWS( 1000 ); } abort( reason) { return this . _closeWS( 4000 , reason&& reason. message); } _closeWS( code, reasonString) { return new Promise(( resolve, reject) => { this . _ws. onclose= e=> { if ( e. wasClean) { resolve(); } else { reject( new Error ( "The connection was not closed cleanly" )); } }; this . _ws. close( code, reasonString); }); } }
이 함수로 생성한 객체를 이용해 표준 스트림 API로 원격 웹 소켓과 통신할 수 있습니다:
const streamyWS= streamifyWebSocket( "wss://example.com:443/" , "protocol" ); const writer= streamyWS. writable. getWriter(); const reader= streamyWS. readable. getReader(); writer. write( "Hello" ); writer. write( "web socket!" ); reader. read(). then(({ value, done}) => { console. log( "웹 소켓 응답: " , value); });
이 구조에서는 readable
을 취소하면 writable
도 암묵적으로 닫히며, writable
을 닫거나 중단하면
readable
역시 암묵적으로 닫히게 됩니다.
앞서 언급한 노트에서 웹 소켓을 스트림으로 래핑하는 방식에 대해 참고하세요.
10.9. 템플릿 태그를 치환하는 변환 스트림
데이터 스트림에서 태그를 변수로 치환하는 것은, 교체해야 할 부분이 전체 데이터에 비해 작은 경우가 많으므로 자주 유용합니다. 이 예시는 그 작업을 간단하게 처리하는 방법을 보여줍니다. 문자열을
문자열로 매핑해서, "Time: {{time}} Message: {{message}}"
와 같은 템플릿을
"Time: 15:36 Message: hello"
로 변환합니다. 단, { time: "15:36", message: "hello" }
가
LipFuzzTransformer
에 substitutions
매개변수로 전달된 경우입니다.
또한 이 예시는, 청크에 부분적인 데이터가 들어있어 더 많은 데이터를 받아야만 변환할 수 있는 상황을 처리하는 한 가지 방법도 보여줍니다. 이 경우, 부분적인 템플릿 태그는
partialChunk
속성에 쌓아두다가 태그의 끝이 발견되거나 스트림이 종료될 때까지 기다립니다.
class LipFuzzTransformer{ constructor ( substitutions) { this . substitutions= substitutions; this . partialChunk= "" ; this . lastIndex= undefined ; } transform( chunk, controller) { chunk= this . partialChunk+ chunk; this . partialChunk= "" ; // lastIndex는 마지막 치환 이후 첫 번째 문자 인덱스입니다. this . lastIndex= 0 ; chunk= chunk. replace( /\{\{([a-zA-Z0-9_-]+)\}\}/g , this . replaceTag. bind( this )); // 문자열 끝에 불완전한 템플릿 태그가 있는지 확인하는 정규식 const partialAtEndRegexp= /\{(\{([a-zA-Z0-9_-]+(\})?)?)?$/g ; // 이미 치환한 문자 이후만 확인 partialAtEndRegexp. lastIndex= this . lastIndex; this . lastIndex= undefined ; const match= partialAtEndRegexp. exec( chunk); if ( match) { this . partialChunk= chunk. substring( match. index); chunk= chunk. substring( 0 , match. index); } controller. enqueue( chunk); } flush( controller) { if ( this . partialChunk. length> 0 ) { controller. enqueue( this . partialChunk); } } replaceTag( match, p1, offset) { let replacement= this . substitutions[ p1]; if ( replacement=== undefined ) { replacement= "" ; } this . lastIndex= offset+ replacement. length; return replacement; } }
이 경우 transformer를 TransformStream
생성자에 클래스 형태로 전달합니다. 인스턴스 데이터 추적이 필요할 때 유용합니다.
이 클래스는 다음과 같이 사용합니다:
const data= { userName, displayName, icon, date}; const ts= new TransformStream( new LipFuzzTransformer( data)); fetchEvent. respondWith( fetch( fetchEvent. request. url). then( response=> { const transformedBody= response. body// 바이너리 응답을 문자열로 디코딩 . pipeThrough( new TextDecoderStream()) // LipFuzzTransformer 적용 . pipeThrough( ts) // 변환된 문자열을 다시 인코딩 . pipeThrough( new TextEncoderStream()); return new Response( transformedBody); }) );
간단히 하기 위해 LipFuzzTransformer
는 텍스트를 이스케이프 없이 치환합니다. 실제 응용에서는 보안과 견고성을 위해 컨텍스트에 맞는
이스케이프를 제공하는 템플릿 시스템을 사용하는 것이 좋습니다.
10.10. 동기 매퍼 함수로 생성된 변환 스트림
다음 함수는 동기 "매퍼" 함수를 사용해 새로운 TransformStream
인스턴스를 생성합니다. 이 매퍼 함수는 보통 Array.prototype.map
에 전달하는 타입입니다.
이 API가 아주 작은 변환에도 간결함을 제공함을 보여줍니다.
function mapperTransformStream( mapperFunction) { return new TransformStream({ transform( chunk, controller) { controller. enqueue( mapperFunction( chunk)); } }); }
이 함수를 사용해 모든 입력을 대문자로 만드는 TransformStream
을 만들 수 있습니다:
const ts= mapperTransformStream( chunk=> chunk. toUpperCase()); const writer= ts. writable. getWriter(); const reader= ts. readable. getReader(); writer. write( "No need to shout" ); // "NO NEED TO SHOUT"가 기록됩니다: reader. read(). then(({ value}) => console. log( value));
동기 변환은 자체적으로 역압력을 일으키지 않지만, 역압력이 없는 경우에만 청크를 변환하므로 자원이 낭비되지 않습니다.
예외는 자연스럽게 스트림에 오류를 발생시킵니다:
const ts= mapperTransformStream( chunk=> JSON. parse( chunk)); const writer= ts. writable. getWriter(); const reader= ts. readable. getReader(); writer. write( "[1, " ); // 두 번 SyntaxError가 기록됩니다: reader. read(). catch ( e=> console. error( e)); writer. write( "{}" ). catch ( e=> console. error( e));
10.11. 새로운 읽기 가능한 스트림을 생성하는 프리미티브로서의 아이덴티티 변환 스트림 활용
아이덴티티 변환
스트림과 pipeTo()
를
결합하면 스트림을 다루는 강력한 방법이 됩니다. 이 섹션은 이런 일반적인 기법의 몇 가지 예시를 포함합니다.
Promise로 된 읽기 가능한 스트림을 실제 읽기 가능한 스트림처럼 다루는 것이 자연스러울 때가 있습니다. 간단한 어댑터 함수 하나면 충분합니다:
function promiseToReadable( promiseForReadable) { const ts= new TransformStream(); promiseForReadable. then( readable=> readable. pipeTo( ts. writable)) . catch ( reason=> ts. writable. abort( reason)) . catch (() => {}); return ts. readable; }
여기서는 데이터를 쓰기 측에 파이프하고 읽기 측을 반환합니다. 파이프가 오류를 일으키면
쓰기 측을 abort하여 반환된 읽기 측에도 오류가 전파됩니다. 만약 pipeTo()
로 이미
쓰기 측에 오류가 발생했다면, abort()
호출은
거부(Reject)를 반환하고, 이는 무시해도 안전합니다.
이보다 더 복잡한 확장 예시는, 여러 읽기 가능한 스트림을 하나로 연결(concatenate)하는 것입니다:
function concatenateReadables( readables) { const ts= new TransformStream(); let promise= Promise. resolve(); for ( const readableof readables) { promise= promise. then( () => readable. pipeTo( ts. writable, { preventClose: true }), reason=> { return Promise. all([ ts. writable. abort( reason), readable. cancel( reason) ]); } ); } promise. then(() => ts. writable. close(), reason=> ts. writable. abort( reason)) . catch (() => {}); return ts. readable; }
여기서 오류 처리는 미묘합니다. 연결된 스트림을 취소하면 모든 입력 스트림도 취소해야 하기 때문입니다. 그러나 성공 케이스는 단순합니다. readables
iterable의
각 스트림을 아이덴티티 변환 스트림의 쓰기 측에
차례로 파이프하고, 마지막에 닫습니다. 읽기 측은
모든 입력 스트림의 청크를 순서대로 연결한 결과가 되어 함수에서 반환합니다. 역압력(backpressure)은 평소와 같이 적용됩니다.
감사의 말씀
편집자들은 다음 분들께 감사의 말씀을 전합니다: Anne van Kesteren, AnthumChris, Arthur Langereis, Ben Kelly, Bert Belder, Brian di Palma, Calvin Metcalf, Dominic Tarr, Ed Hager, Eric Skoglund, Forbes Lindesay, Forrest Norvell, Gary Blackwood, Gorgi Kosev, Gus Caplan, 贺师俊 (hax), Isaac Schlueter, isonmad, Jake Archibald, Jake Verbaten, James Pryor, Janessa Det, Jason Orendorff, Jeffrey Yasskin, Jeremy Roman, Jens Nockert, Lennart Grahl, Luca Casonato, Mangala Sadhu Sangeet Singh Khalsa, Marcos Caceres, Marvin Hagemeister, Mattias Buelens, Michael Mior, Mihai Potra, Nidhi Jaju, Romain Bellessort, Simon Menke, Stephen Sugden, Surma, Tab Atkins, Tanguy Krotoff, Thorsten Lorenz, Till Schneidereit, Tim Caswell, Trevor Norris, tzik, Will Chan, Youenn Fablet, 평野裕 (Yutaka Hirano), 그리고 Xabier Rodríguez 이 명세에 기여해 주신 모든 분들께 감사드립니다. 커뮤니티의 적극적인 참여가 있었기에 이 명세가 완성될 수 있었습니다. 여러분 없이는 불가능했습니다.
이 표준은 Adam Rice(Google, ricea@chromium.org), Domenic Denicola(Google, d@domenic.me), Mattias Buelens, 그리고 吉野剛史(Takeshi Yoshino, tyoshino@chromium.org)가 작성하였습니다.
지적 재산권
Copyright © WHATWG (Apple, Google, Mozilla, Microsoft). 본 문서는 크리에이티브 커먼즈 저작자 표시 4.0 국제 라이선스에 따라 라이선스가 부여됩니다. 일부 내용이 소스 코드에 통합되는 경우, 해당 부분은 BSD 3-클라우즈 라이선스로 소스 코드에 적용됩니다.
이 문서는 현행 표준입니다. 특허 검토 버전에 관심이 있으신 분은 현행 표준 검토 초안을 참조하시기 바랍니다.