From edea0ec301f81a0ac2276e808179426f78147e47 Mon Sep 17 00:00:00 2001 From: Richard Moore <github@ricmoo.com> Date: Fri, 2 Apr 2021 03:53:48 -0400 Subject: [PATCH] Added experimental _JsonRpcBatchProvider (#62, #656, #892). --- .../src.ts/json-rpc-batch-provider.ts | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 packages/providers/src.ts/json-rpc-batch-provider.ts diff --git a/packages/providers/src.ts/json-rpc-batch-provider.ts b/packages/providers/src.ts/json-rpc-batch-provider.ts new file mode 100644 index 0000000000..29f3755584 --- /dev/null +++ b/packages/providers/src.ts/json-rpc-batch-provider.ts @@ -0,0 +1,97 @@ + +import { deepCopy } from "@ethersproject/properties"; +import { fetchJson } from "@ethersproject/web"; + +import { JsonRpcProvider } from "./json-rpc-provider"; + +// Experimental + +export class _JsonRpcBatchProvider extends JsonRpcProvider { + _pendingBatchAggregator: NodeJS.Timer; + _pendingBatch: Array<{ + request: { method: string, params: Array<any>, id: number, jsonrpc: "2.0" }, + resolve: (result: any) => void, + reject: (error: Error) => void + }>; + + send(method: string, params: Array<any>): Promise<any> { + const request = { + method: method, + params: params, + id: (this._nextId++), + jsonrpc: "2.0" + }; + + if (this._pendingBatch == null) { + this._pendingBatch = [ ]; + } + + const inflightRequest: any = { request, resolve: null, reject: null }; + + const promise = new Promise((resolve, reject) => { + inflightRequest.resolve = resolve; + inflightRequest.reject = reject; + }); + + this._pendingBatch.push(inflightRequest); + + if (!this._pendingBatchAggregator) { + // Schedule batch for next event loop + short duration + this._pendingBatchAggregator = setTimeout(() => { + + // Get teh current batch and clear it, so new requests + // go into the next batch + const batch = this._pendingBatch; + this._pendingBatch = null; + this._pendingBatchAggregator = null; + + // Get the request as an array of requests + const request = batch.map((inflight) => inflight.request); + + this.emit("debug", { + action: "requestBatch", + request: deepCopy(request), + provider: this + }); + + return fetchJson(this.connection, JSON.stringify(request)).then((result) => { + this.emit("debug", { + action: "response", + request: request, + response: result, + provider: this + }); + + // For each result, feed it to the correct Promise, depending + // on whether it was a success or error + batch.forEach((inflightRequest, index) => { + const payload = result[index]; + if (payload.error) { + const error = new Error(payload.error.message); + (<any>error).code = payload.error.code; + (<any>error).data = payload.error.data; + inflightRequest.reject(error); + } else { + inflightRequest.resolve(payload.result); + } + }); + + }, (error) => { + this.emit("debug", { + action: "response", + error: error, + request: request, + provider: this + }); + + batch.forEach((inflightRequest) => { + inflightRequest.reject(error); + }); + }); + + }, 10); + } + + return promise; + } +}