ethers.js/packages/providers/src.ts/json-rpc-batch-provider.ts

98 lines
3.3 KiB
TypeScript
Raw Permalink Normal View History

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;
}
}