UNPKG

@v4fire/core

Version:
246 lines (177 loc) 6.71 kB
# core/request/response This module provides a class to work with server response data. The class' API is pretty similar to native the [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) class but has some extra functionality. The class doesn't implement native Response static methods. ## Why not just use the native Response class? Because, the `core/request` module can use different engines, but not only `fetch` or `XMLHttpRequest`. These engines can have different peculiarities, so we need to create a new abstraction. ## Difference between native and V4 Response classes There are a few differences between these classes: 1. The `body` property is not a stream object. Instead, it contains raw response data used to initialize an instance. 2. Each `Response` instance is an async iterable object. So you can use this feature to process the response as a stream. Notice, not every request engine can stream response. ```js import request from 'core/request'; import fetchEngine from 'core/request/engines/fetch'; for await (const chunk of request('//movie', {engine: fetchEngine})) { console.log(chunk.data, chunk.total, chunk.loaded); } ``` ## Extra API ### Events | EventName | Description | Payload description | Payload | |----------------|-------------------------------------------------------|---------------------|---------| | `bodyUsed` | The response body has been read via `decode` | - | - | | `streamUsed` | The response body has been read via an async iterator | - | - | ### Constructor options With creation of a Response instance you can pass a bunch of options. ```typescript export interface ResponseOptions { url?: string; redirected?: boolean; type?: ResponseModeType; /** * Parent operation promise */ parent?: AbortablePromise; /** * A meta flag that indicates that the request is important: is usually used with decoders to indicate that * the request needs to be executed as soon as possible */ important?: boolean; status?: StatusCodes; statusText?: string; /** * A list of status codes (or a single code) that match successful operation. * Also, you can pass a range of codes. */ okStatuses?: OkStatuses; /** * Type of the response data */ responseType?: ResponseType; /** * Set of response headers */ headers?: RawHeaders; /** * A function or sequence of functions to decode a response body */ decoder?: WrappedDecoder | WrappedDecoders; /** * A function or sequence of functions to decode a response chunk when you are parsing the response in a stream form */ streamDecoder?: WrappedStreamDecoder | WrappedStreamDecoders; /** * Reviver function for `JSON.parse` * @default `convertIfDate` */ jsonReviver?: JSONCb | false; } ``` ### Properties #### streamUsed True, if the response body is already read as a stream. #### okStatuses A list of status codes (or a single code) that match successful operation. Also, you can pass a range of codes. #### decoders A list of response decoders. #### streamDecoders A list of response decoders to apply for chunks when you are parsing response in a stream form. #### jsonReviver A reviver function for `JSON.parse`. #### emitter Event emitter to broadcast response events. ```js import request from 'core/request'; request('//foo.jpg') .then(({response}) => { response.emitter.once('bodyUsed', () => { console.log('Body has been read'); }); return response.decode(); }); ``` ### Methods #### decode Parses the response body and returns a promise with the result. The operation result is memoized, and you can't parse the response as a stream after invoking this method. A way to parse data is based on the response `Content-Type` header or a passed `responseType` constructor option. Also, a sequence of decoders is applied to the parsed result if they are passed with a `decoders`constructor option. ```js import request from 'core/request'; request('//foo.jpg') .then(({response}) => response.decode()) .then((img) => console.log(img instanceof Blob)); request('//users', {decoders: [parseProtobuf, normalizeUsers], responseType: 'arrayBuffer'}) .then(({response}) => response.decode()) .then((users) => console.log(users)); ``` #### decodeStream Parses the response body as a stream and yields chunks via an async iterator. You can't parse the response as a whole data after invoking this method. A way to parse data chunks is based on the response `Content-Type` header or a passed `responseType`constructor option. Also, a sequence of stream decoders is applied to the parsed chunk if they are passed with a `streamDecoders` constructor option. ```js import request from 'core/request'; import { streamArray } from 'core/json/stream'; request('//foo.jpg') .then(async ({response}) => { for await (const chunk of response.decodeStream()) { console.log(chunk instanceof ArrayBuffer); } }); request('//users', {streamDecoders: [streamArray], responseType: 'json'}) .then(async ({response}) => { for await (const user of response.decodeStream()) { console.log(user); } }); ``` #### jsonStream Parses the response data stream as a JSON tokens and yields them via an async iterator. ```js import request from 'core/request'; import { streamArray } from 'core/json/stream'; request('//users', {streamDecoders: [streamArray]}) .then(async ({response}) => { for await (const user of response.jsonStream()) { console.log(user); } }); ``` #### textStream Parses the response data stream as a text chunks and yields them via an async iterator. ```js import request from 'core/request'; request('//users') .then(async ({response}) => { for await (const textFragment of response.textStream()) { console.log(textFragment); } }); ``` #### stream Parses the response data stream as an ArrayBuffer chunks and yields them via an async iterator. ```js import request from 'core/request'; request('//users') .then(async ({response}) => { for await (const chunkBuffer of response.stream()) { console.log(chunkBuffer); } }); ``` #### [Symbol.asyncIterator] Returns an iterator by the response body. Mind, when you parse response via iterator, you won't be able to use other parse methods, like `json` or `text`. ```js import request from 'core/request'; request('//users') .then(async ({response}) => { for await (const {loaded, total, data} of response) { console.log(loaded, total, data); } }); ```