Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/vitalets/websocket-as-promised

A Promise-based API for WebSockets
https://github.com/vitalets/websocket-as-promised

javascript nodejs promise websocket websocket-api websocket-client

Last synced: about 8 hours ago
JSON representation

A Promise-based API for WebSockets

Awesome Lists containing this project

README

        

# websocket-as-promised

websocket-as-promised logo

[![Actions Status](https://github.com/vitalets/websocket-as-promised/workflows/autotests/badge.svg)](https://github.com/vitalets/websocket-as-promised/actions)
[![Known Vulnerabilities](https://snyk.io/test/github/vitalets/websocket-as-promised/badge.svg?targetFile=package.json)](https://snyk.io/test/github/vitalets/websocket-as-promised?targetFile=package.json)
[![npm](https://img.shields.io/npm/v/websocket-as-promised.svg)](https://www.npmjs.com/package/websocket-as-promised)
[![license](https://img.shields.io/npm/l/websocket-as-promised.svg)](https://www.npmjs.com/package/websocket-as-promised)

A [WebSocket] client library with [Promise]-based API for browser and Node.js.

## Example
```js
import WebSocketAsPromised from 'websocket-as-promised';

// create instance of WebSocket connection
const wsp = new WebSocketAsPromised('ws://example.com');

// wait for WebSocket connection to open
await wsp.open();

// send some data
wsp.send('data');

// wait for connection to close
await wsp.close();
```

## Contents
* [Requirements](#requirements)
* [Installation](#installation)
* [Usage](#usage-in-browser)
* [Browser](#usage-in-browser)
* [Node.js](#usage-in-nodejs)
* [Sending data](#sending-raw-data)
* [raw](#sending-raw-data)
* [JSON](#sending-json)
* [binary](#sending-binary)
* [request / response](#sending-requests)
* [API](#api)
* [Changelog](#changelog)
* [License](#license)

## Requirements
* global `Promise` constructor

## Installation
```bash
npm i websocket-as-promised --save
```

## Usage in browser
```js
import WebSocketAsPromised from 'websocket-as-promised';

const wsp = new WebSocketAsPromised('ws://example.com');

wsp.open()
.then(() => wsp.send('message'))
.then(() => wsp.close())
.catch(e => console.error(e));
```
Or with ES7 [async / await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function):
```js
import WebSocketAsPromised from 'websocket-as-promised';

const wsp = new WebSocketAsPromised('ws://example.com');

(async () => {
try {
await wsp.open();
wsp.send('message');
} catch(e) {
console.error(e);
} finally {
await wsp.close();
}
})();
```

## Usage in Node.js
As there is no built-in WebSocket client in Node.js, you should use a third-party WebSocket npm package.

### Usage with [websocket](https://www.npmjs.com/package/websocket)
Here you should use W3C compatible client - [W3CWebSocket](https://github.com/theturtle32/WebSocket-Node/blob/master/docs/W3CWebSocket.md):
```js
const WebSocketAsPromised = require('websocket-as-promised');
const W3CWebSocket = require('websocket').w3cwebsocket;

const wsp = new WebSocketAsPromised('ws://example.com', {
createWebSocket: url => new W3CWebSocket(url)
});

wsp.open()
.then(() => wsp.send('message'))
.then(() => wsp.close())
.catch(e => console.error(e));
```

### Usage with [ws](https://www.npmjs.com/package/ws)
Here it is important to define `extractMessageData` option as event data are passed directly to `onmessage` handler,
not as `event.data` by [spec](https://html.spec.whatwg.org/multipage/comms.html#dom-messageevent-data):
```js
const WebSocketAsPromised = require('websocket-as-promised');
const WebSocket = require('ws');

const wsp = new WebSocketAsPromised('ws://example.com', {
createWebSocket: url => new WebSocket(url),
extractMessageData: event => event, // <- this is important
});

wsp.open()
.then(() => wsp.send('message'))
.then(() => wsp.close())
.catch(e => console.error(e));
```

## Sending raw data
To send raw data use `.send()` method:
```js
wsp.send('foo');
```
To handle raw data from server use `.onMessage` channel:
```js
wsp.onMessage.addListener(data => console.log(data));
```

## Sending JSON
To send JSON you should define `packMessage / unpackMessage` options:
```js
const wsp = new WebSocketAsPromised(wsUrl, {
packMessage: data => JSON.stringify(data),
unpackMessage: data => JSON.parse(data)
});
```
To send data use `.sendPacked()` method passing json as parameter:
```js
wsp.sendPacked({foo: 'bar'});
```
To read unpacked data from received message you can subscribe to `onUnpackedMessage` channel:
```js
wsp.onUnpackedMessage.addListener(data => console.log(data.status));
```

## Sending binary
Example of sending `Uint8Array`:
```js
const wsp = new WebSocketAsPromised(wsUrl, {
packMessage: data => (new Uint8Array(data)).buffer,
unpackMessage: data => new Uint8Array(data),
});

wsp.open()
.then(() => wsp.sendPacked([1, 2, 3]))
.then(() => wsp.close())
.catch(e => console.error(e));
```

## Sending requests
*websocket-as-promised* provides simple request-response mechanism ([JSON RPC](https://www.jsonrpc.org)).
Method `.sendRequest()` sends message with unique `requestId` and returns promise.
That promise get resolved when response message with the same `requestId` comes.
For reading/setting `requestId` from/to message there are two functions defined in options `attachRequestId / extractRequestId`:
```js
const wsp = new WebSocketAsPromised(wsUrl, {
packMessage: data => JSON.stringify(data),
unpackMessage: data => JSON.parse(data),
attachRequestId: (data, requestId) => Object.assign({id: requestId}, data), // attach requestId to message as `id` field
extractRequestId: data => data && data.id, // read requestId from message `id` field
});

wsp.open()
.then(() => wsp.sendRequest({foo: 'bar'})) // actually sends {foo: 'bar', id: 'xxx'}, because `attachRequestId` defined above
.then(response => console.log(response)); // waits server message with corresponding requestId: {id: 'xxx', ...}
```
By default `requestId` value is auto-generated, but you can set it manually:
```js
wsp.sendRequest({foo: 'bar'}, {requestId: 42});
```

> Note: you should implement yourself attaching `requestId` on server side.

## API

### Classes


WebSocketAsPromised


### Typedefs



Options : Object


### WebSocketAsPromised
**Kind**: global class

* [WebSocketAsPromised](#WebSocketAsPromised)
* [new WebSocketAsPromised(url, [options])](#new_WebSocketAsPromised_new)
* [.ws](#WebSocketAsPromised+ws) ⇒ WebSocket
* [.url](#WebSocketAsPromised+url) ⇒ String
* [.isOpening](#WebSocketAsPromised+isOpening) ⇒ Boolean
* [.isOpened](#WebSocketAsPromised+isOpened) ⇒ Boolean
* [.isClosing](#WebSocketAsPromised+isClosing) ⇒ Boolean
* [.isClosed](#WebSocketAsPromised+isClosed) ⇒ Boolean
* [.onOpen](#WebSocketAsPromised+onOpen) ⇒ Channel
* [.onSend](#WebSocketAsPromised+onSend) ⇒ Channel
* [.onMessage](#WebSocketAsPromised+onMessage) ⇒ Channel
* [.onUnpackedMessage](#WebSocketAsPromised+onUnpackedMessage) ⇒ Channel
* [.onResponse](#WebSocketAsPromised+onResponse) ⇒ Channel
* [.onClose](#WebSocketAsPromised+onClose) ⇒ Channel
* [.onError](#WebSocketAsPromised+onError) ⇒ Channel
* [.open()](#WebSocketAsPromised+open) ⇒ Promise.<Event>
* [.sendRequest(data, [options])](#WebSocketAsPromised+sendRequest) ⇒ Promise
* [.sendPacked(data)](#WebSocketAsPromised+sendPacked)
* [.send(data)](#WebSocketAsPromised+send)
* [.waitUnpackedMessage(predicate, [options])](#WebSocketAsPromised+waitUnpackedMessage) ⇒ Promise
* [.close([code], [reason])](#WebSocketAsPromised+close) ⇒ Promise.<Event>
* [.removeAllListeners()](#WebSocketAsPromised+removeAllListeners)

#### new WebSocketAsPromised(url, [options])
Constructor. Unlike original WebSocket it does not immediately open connection.
Please call `open()` method to connect.

| Param | Type | Description |
| --- | --- | --- |
| url | String | WebSocket URL |
| [options] | [Options](#Options) | |

#### wsp.ws ⇒ WebSocket
Returns original WebSocket instance created by `options.createWebSocket`.

**Kind**: instance property of [WebSocketAsPromised](#WebSocketAsPromised)

#### wsp.url ⇒ String
Returns WebSocket url.

**Kind**: instance property of [WebSocketAsPromised](#WebSocketAsPromised)

#### wsp.isOpening ⇒ Boolean
Is WebSocket connection in opening state.

**Kind**: instance property of [WebSocketAsPromised](#WebSocketAsPromised)

#### wsp.isOpened ⇒ Boolean
Is WebSocket connection opened.

**Kind**: instance property of [WebSocketAsPromised](#WebSocketAsPromised)

#### wsp.isClosing ⇒ Boolean
Is WebSocket connection in closing state.

**Kind**: instance property of [WebSocketAsPromised](#WebSocketAsPromised)

#### wsp.isClosed ⇒ Boolean
Is WebSocket connection closed.

**Kind**: instance property of [WebSocketAsPromised](#WebSocketAsPromised)

#### wsp.onOpen ⇒ Channel
Event channel triggered when connection is opened.

**Kind**: instance property of [WebSocketAsPromised](#WebSocketAsPromised)
**See**: https://vitalets.github.io/chnl/#channel
**Example**
```js
wsp.onOpen.addListener(() => console.log('Connection opened'));
```

#### wsp.onSend ⇒ Channel
Event channel triggered every time when message is sent to server.

**Kind**: instance property of [WebSocketAsPromised](#WebSocketAsPromised)
**See**: https://vitalets.github.io/chnl/#channel
**Example**
```js
wsp.onSend.addListener(data => console.log('Message sent', data));
```

#### wsp.onMessage ⇒ Channel
Event channel triggered every time when message received from server.

**Kind**: instance property of [WebSocketAsPromised](#WebSocketAsPromised)
**See**: https://vitalets.github.io/chnl/#channel
**Example**
```js
wsp.onMessage.addListener(message => console.log(message));
```

#### wsp.onUnpackedMessage ⇒ Channel
Event channel triggered every time when received message is successfully unpacked.
For example, if you are using JSON transport, the listener will receive already JSON parsed data.

**Kind**: instance property of [WebSocketAsPromised](#WebSocketAsPromised)
**See**: https://vitalets.github.io/chnl/#channel
**Example**
```js
wsp.onUnpackedMessage.addListener(data => console.log(data.foo));
```

#### wsp.onResponse ⇒ Channel
Event channel triggered every time when response to some request comes.
Received message considered a response if requestId is found in it.

**Kind**: instance property of [WebSocketAsPromised](#WebSocketAsPromised)
**See**: https://vitalets.github.io/chnl/#channel
**Example**
```js
wsp.onResponse.addListener(data => console.log(data));
```

#### wsp.onClose ⇒ Channel
Event channel triggered when connection closed.
Listener accepts single argument `{code, reason}`.

**Kind**: instance property of [WebSocketAsPromised](#WebSocketAsPromised)
**See**: https://vitalets.github.io/chnl/#channel
**Example**
```js
wsp.onClose.addListener(event => console.log(`Connections closed: ${event.reason}`));
```

#### wsp.onError ⇒ Channel
Event channel triggered when by Websocket 'error' event.

**Kind**: instance property of [WebSocketAsPromised](#WebSocketAsPromised)
**See**: https://vitalets.github.io/chnl/#channel
**Example**
```js
wsp.onError.addListener(event => console.error(event));
```

#### wsp.open() ⇒ Promise.<Event>
Opens WebSocket connection. If connection already opened, promise will be resolved with "open event".

**Kind**: instance method of [WebSocketAsPromised](#WebSocketAsPromised)

#### wsp.sendRequest(data, [options]) ⇒ Promise
Performs request and waits for response.

**Kind**: instance method of [WebSocketAsPromised](#WebSocketAsPromised)

| Param | Type | Default |
| --- | --- | --- |
| data | \* | |
| [options] | Object | |
| [options.requestId] | String \| Number | <auto-generated> |
| [options.timeout] | Number | 0 |

#### wsp.sendPacked(data)
Packs data with `options.packMessage` and sends to the server.

**Kind**: instance method of [WebSocketAsPromised](#WebSocketAsPromised)

| Param | Type |
| --- | --- |
| data | \* |

#### wsp.send(data)
Sends data without packing.

**Kind**: instance method of [WebSocketAsPromised](#WebSocketAsPromised)

| Param | Type |
| --- | --- |
| data | String \| Blob \| ArrayBuffer |

#### wsp.waitUnpackedMessage(predicate, [options]) ⇒ Promise
Waits for particular message to come.

**Kind**: instance method of [WebSocketAsPromised](#WebSocketAsPromised)

| Param | Type | Default | Description |
| --- | --- | --- | --- |
| predicate | function | | function to check incoming message. |
| [options] | Object | | |
| [options.timeout] | Number | 0 | |
| [options.timeoutError] | Error | | |

**Example**
```js
const response = await wsp.waitUnpackedMessage(data => data && data.foo === 'bar');
```

#### wsp.close([code], [reason]) ⇒ Promise.<Event>
Closes WebSocket connection. If connection already closed, promise will be resolved with "close event".

**Kind**: instance method of [WebSocketAsPromised](#WebSocketAsPromised)

| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [code] | number | 1000 | A numeric value indicating the status code. |
| [reason] | string | | A human-readable reason for closing connection. |

#### wsp.removeAllListeners()
Removes all listeners from WSP instance. Useful for cleanup.

**Kind**: instance method of [WebSocketAsPromised](#WebSocketAsPromised)

### Options : Object
**Kind**: global typedef
**Defaults**: please see [options.js](https://github.com/vitalets/websocket-as-promised/blob/master/src/options.js)
**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| [createWebSocket] | function | url => new WebSocket(url) | custom function for WebSocket construction. |
| [packMessage] | function | noop | packs message for sending. For example, `data => JSON.stringify(data)`. |
| [unpackMessage] | function | noop | unpacks received message. For example, `data => JSON.parse(data)`. |
| [attachRequestId] | function | noop | injects request id into data. For example, `(data, requestId) => Object.assign({requestId}, data)`. |
| [extractRequestId] | function | noop | extracts request id from received data. For example, `data => data.requestId`. |
| [extractMessageData] | function | event => event.data | extracts data from event object. |
| timeout | Number | 0 | timeout for opening connection and sending messages. |
| connectionTimeout | Number | 0 | special timeout for opening connection, by default equals to `timeout`. |

## Changelog
Please see [CHANGELOG.md](CHANGELOG.md).

## License
MIT @ [Vitaliy Potapov](https://github.com/vitalets)


* * *

If you love :heart: JavaScript and would like to track new trending repositories,

have a look on vitalets/github-trending-repos.

[Promise]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
[WebSocket]: https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API