UNPKG

6.33 kBJavaScriptView Raw
1// Copyright 2017-2022 @polkadot/api authors & contributors
2// SPDX-License-Identifier: Apache-2.0
3
4import { objectSpread } from '@polkadot/util';
5import { ApiBase } from "../base/index.js";
6import { Combinator } from "./Combinator.js";
7import { promiseTracker, toPromiseMethod } from "./decorateMethod.js";
8
9/**
10 * # @polkadot/api/promise
11 *
12 * ## Overview
13 *
14 * @name ApiPromise
15 * @description
16 * ApiPromise is a standard JavaScript wrapper around the RPC and interfaces on the Polkadot network. As a full Promise-based, all interface calls return Promises, including the static `.create(...)`. Subscription calls utilise `(value) => {}` callbacks to pass through the latest values.
17 *
18 * The API is well suited to real-time applications where either the single-shot state is needed or use is to be made of the subscription-based features of Polkadot (and Substrate) clients.
19 *
20 * @see [[ApiRx]]
21 *
22 * ## Usage
23 *
24 * Making rpc calls -
25 * <BR>
26 *
27 * ```javascript
28 * import ApiPromise from '@polkadot/api/promise';
29 *
30 * // initialise via static create
31 * const api = await ApiPromise.create();
32 *
33 * // make a subscription to the network head
34 * api.rpc.chain.subscribeNewHeads((header) => {
35 * console.log(`Chain is at #${header.number}`);
36 * });
37 * ```
38 * <BR>
39 *
40 * Subscribing to chain state -
41 * <BR>
42 *
43 * ```javascript
44 * import { ApiPromise, WsProvider } from '@polkadot/api';
45 *
46 * // initialise a provider with a specific endpoint
47 * const provider = new WsProvider('wss://example.com:9944')
48 *
49 * // initialise via isReady & new with specific provider
50 * const api = await new ApiPromise({ provider }).isReady;
51 *
52 * // retrieve the block target time
53 * const blockPeriod = await api.query.timestamp.blockPeriod().toNumber();
54 * let last = 0;
55 *
56 * // subscribe to the current block timestamp, updates automatically (callback provided)
57 * api.query.timestamp.now((timestamp) => {
58 * const elapsed = last
59 * ? `, ${timestamp.toNumber() - last}s since last`
60 * : '';
61 *
62 * last = timestamp.toNumber();
63 * console.log(`timestamp ${timestamp}${elapsed} (${blockPeriod}s target)`);
64 * });
65 * ```
66 * <BR>
67 *
68 * Submitting a transaction -
69 * <BR>
70 *
71 * ```javascript
72 * import ApiPromise from '@polkadot/api/promise';
73 *
74 * ApiPromise.create().then((api) => {
75 * const [nonce] = await api.query.system.account(keyring.alice.address);
76 *
77 * api.tx.balances
78 * // create transfer
79 * transfer(keyring.bob.address, 12345)
80 * // sign the transcation
81 * .sign(keyring.alice, { nonce })
82 * // send the transaction (optional status callback)
83 * .send((status) => {
84 * console.log(`current status ${status.type}`);
85 * })
86 * // retrieve the submitted extrinsic hash
87 * .then((hash) => {
88 * console.log(`submitted with hash ${hash}`);
89 * });
90 * });
91 * ```
92 */
93export class ApiPromise extends ApiBase {
94 #isReadyPromise;
95 #isReadyOrErrorPromise;
96
97 /**
98 * @description Creates an instance of the ApiPromise class
99 * @param options Options to create an instance. This can be either [[ApiOptions]] or
100 * an [[WsProvider]].
101 * @example
102 * <BR>
103 *
104 * ```javascript
105 * import Api from '@polkadot/api/promise';
106 *
107 * new Api().isReady.then((api) => {
108 * api.rpc.subscribeNewHeads((header) => {
109 * console.log(`new block #${header.number.toNumber()}`);
110 * });
111 * });
112 * ```
113 */
114 constructor(options) {
115 super(options, 'promise', toPromiseMethod);
116 this.#isReadyPromise = new Promise(resolve => {
117 super.once('ready', () => resolve(this));
118 });
119 this.#isReadyOrErrorPromise = new Promise((resolve, reject) => {
120 const tracker = promiseTracker(resolve, reject);
121 super.once('ready', () => tracker.resolve(this));
122 super.once('error', error => tracker.reject(error));
123 });
124 }
125
126 /**
127 * @description Creates an ApiPromise instance using the supplied provider. Returns an Promise containing the actual Api instance.
128 * @param options options that is passed to the class contructor. Can be either [[ApiOptions]] or a
129 * provider (see the constructor arguments)
130 * @example
131 * <BR>
132 *
133 * ```javascript
134 * import Api from '@polkadot/api/promise';
135 *
136 * Api.create().then(async (api) => {
137 * const timestamp = await api.query.timestamp.now();
138 *
139 * console.log(`lastest block timestamp ${timestamp}`);
140 * });
141 * ```
142 */
143 static create(options) {
144 const instance = new ApiPromise(options);
145 if (options && options.throwOnConnect) {
146 return instance.isReadyOrError;
147 }
148
149 // Swallow any rejections on isReadyOrError
150 // (in Node 15.x this creates issues, when not being looked at)
151 instance.isReadyOrError.catch(() => {
152 // ignore
153 });
154 return instance.isReady;
155 }
156
157 /**
158 * @description Promise that resolves the first time we are connected and loaded
159 */
160 get isReady() {
161 return this.#isReadyPromise;
162 }
163
164 /**
165 * @description Promise that resolves if we can connect, or reject if there is an error
166 */
167 get isReadyOrError() {
168 return this.#isReadyOrErrorPromise;
169 }
170
171 /**
172 * @description Returns a clone of this ApiPromise instance (new underlying provider connection)
173 */
174 clone() {
175 return new ApiPromise(objectSpread({}, this._options, {
176 source: this
177 }));
178 }
179
180 /**
181 * @description Creates a combinator that can be used to combine the latest results from multiple subscriptions
182 * @param fns An array of function to combine, each in the form of `(cb: (value: void)) => void`
183 * @param callback A callback that will return an Array of all the values this combinator has been applied to
184 * @example
185 * <BR>
186 *
187 * ```javascript
188 * const address = '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFacT7';
189 *
190 * // combines values from balance & nonce as it updates
191 * api.combineLatest([
192 * api.rpc.chain.subscribeNewHeads,
193 * (cb) => api.query.system.account(address, cb)
194 * ], ([head, [balance, nonce]]) => {
195 * console.log(`#${head.number}: You have ${balance.free} units, with ${nonce} transactions sent`);
196 * });
197 * ```
198 */
199 // eslint-disable-next-line @typescript-eslint/require-await
200 async combineLatest(fns, callback) {
201 const combinator = new Combinator(fns, callback);
202 return () => {
203 combinator.unsubscribe();
204 };
205 }
206}
\No newline at end of file