UNPKG

3.15 kBJavaScriptView Raw
1// Copyright 2017-2022 @polkadot/api authors & contributors
2// SPDX-License-Identifier: Apache-2.0
3
4import { catchError, EMPTY, tap } from 'rxjs';
5import { isFunction, nextTick } from '@polkadot/util';
6// a Promise completion tracker, wrapping an isComplete variable that ensures
7// that the promise only resolves once
8export function promiseTracker(resolve, reject) {
9 let isCompleted = false;
10 return {
11 reject: error => {
12 if (!isCompleted) {
13 isCompleted = true;
14 reject(error);
15 }
16 return EMPTY;
17 },
18 resolve: value => {
19 if (!isCompleted) {
20 isCompleted = true;
21 resolve(value);
22 }
23 }
24 };
25}
26
27// extract the arguments and callback params from a value array possibly containing a callback
28function extractArgs(args, needsCallback) {
29 const actualArgs = args.slice();
30
31 // If the last arg is a function, we pop it, put it into callback.
32 // actualArgs will then hold the actual arguments to be passed to `method`
33 const callback = args.length && isFunction(args[args.length - 1]) ? actualArgs.pop() : undefined;
34
35 // When we need a subscription, ensure that a valid callback is actually passed
36 if (needsCallback && !isFunction(callback)) {
37 throw new Error('Expected a callback to be passed with subscriptions');
38 }
39 return [actualArgs, callback];
40}
41
42// Decorate a call for a single-shot result - retrieve and then immediate unsubscribe
43function decorateCall(method, args) {
44 return new Promise((resolve, reject) => {
45 // single result tracker - either reject with Error or resolve with Codec result
46 const tracker = promiseTracker(resolve, reject);
47
48 // encoding errors reject immediately, any result unsubscribes and resolves
49 const subscription = method(...args).pipe(catchError(error => tracker.reject(error))).subscribe(result => {
50 tracker.resolve(result);
51 nextTick(() => subscription.unsubscribe());
52 });
53 });
54}
55
56// Decorate a subscription where we have a result callback specified
57function decorateSubscribe(method, args, resultCb) {
58 return new Promise((resolve, reject) => {
59 // either reject with error or resolve with unsubscribe callback
60 const tracker = promiseTracker(resolve, reject);
61
62 // errors reject immediately, the first result resolves with an unsubscribe promise, all results via callback
63 const subscription = method(...args).pipe(catchError(error => tracker.reject(error)), tap(() => tracker.resolve(() => subscription.unsubscribe()))).subscribe(result => {
64 // queue result (back of queue to clear current)
65 nextTick(() => resultCb(result));
66 });
67 });
68}
69
70/**
71 * @description Decorate method for ApiPromise, where the results are converted to the Promise equivalent
72 */
73export function toPromiseMethod(method, options) {
74 const needsCallback = !!(options && options.methodName && options.methodName.includes('subscribe'));
75 return function (...args) {
76 const [actualArgs, resultCb] = extractArgs(args, needsCallback);
77 return resultCb ? decorateSubscribe(method, actualArgs, resultCb) : decorateCall((options == null ? void 0 : options.overrideNoSub) || method, actualArgs);
78 };
79}
\No newline at end of file