@v4fire/core
Version:
V4Fire core library
246 lines (177 loc) • 6.71 kB
Markdown
# 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);
}
});
```