Added more robust poll event to Provider.

This commit is contained in:
Richard Moore 2020-05-04 22:43:44 -04:00
parent 86670eb80e
commit dc48bfb7ad
No known key found for this signature in database
GPG Key ID: 665176BE8E9DC651
3 changed files with 48 additions and 13 deletions

@ -36,7 +36,7 @@ import { Bytes, BytesLike, Hexable } from "@ethersproject/bytes"
import { Mnemonic } from "@ethersproject/hdnode"; import { Mnemonic } from "@ethersproject/hdnode";
import { EncryptOptions, ProgressCallback } from "@ethersproject/json-wallets"; import { EncryptOptions, ProgressCallback } from "@ethersproject/json-wallets";
import { Utf8ErrorFunc } from "@ethersproject/strings"; import { Utf8ErrorFunc } from "@ethersproject/strings";
import { ConnectionInfo, FetchJsonResponse, OnceBlockable, PollOptions } from "@ethersproject/web"; import { ConnectionInfo, FetchJsonResponse, OnceBlockable, OncePollable, PollOptions } from "@ethersproject/web";
//////////////////////// ////////////////////////
// Exports // Exports
@ -184,6 +184,7 @@ export {
ConnectionInfo, ConnectionInfo,
OnceBlockable, OnceBlockable,
OncePollable,
PollOptions, PollOptions,
FetchJsonResponse, FetchJsonResponse,

@ -111,6 +111,7 @@ function getTime() {
/** /**
* EventType * EventType
* - "block" * - "block"
* - "poll"
* - "pending" * - "pending"
* - "error" * - "error"
* - filter * - filter
@ -164,7 +165,7 @@ export class Event {
} }
pollable(): boolean { pollable(): boolean {
return (this.tag.indexOf(":") >= 0 || this.tag === "block" || this.tag === "pending"); return (this.tag.indexOf(":") >= 0 || this.tag === "block" || this.tag === "pending" || this.tag === "poll");
} }
} }
@ -196,6 +197,7 @@ export class BaseProvider extends Provider {
_pollingInterval: number; _pollingInterval: number;
_poller: NodeJS.Timer; _poller: NodeJS.Timer;
_bootstrapPoll: NodeJS.Timer;
_lastBlockNumber: number; _lastBlockNumber: number;
@ -328,17 +330,23 @@ export class BaseProvider extends Provider {
async poll(): Promise<void> { async poll(): Promise<void> {
const pollId = nextPollId++; const pollId = nextPollId++;
this.emit("willPoll", pollId); this.emit("willPoll", pollId);
// Track all running promises, so we can trigger a post-poll once they are complete // Track all running promises, so we can trigger a post-poll once they are complete
const runners: Array<Promise<void>> = []; const runners: Array<Promise<void>> = [];
const blockNumber = await this._getInternalBlockNumber(100 + this.pollingInterval / 2); const blockNumber = await this._getInternalBlockNumber(100 + this.pollingInterval / 2);
this._setFastBlockNumber(blockNumber); this._setFastBlockNumber(blockNumber);
// Emit a poll event after we have the latest (fast) block number
this.emit("poll", pollId, blockNumber);
// If the block has not changed, meh. // If the block has not changed, meh.
if (blockNumber === this._lastBlockNumber) { return; } if (blockNumber === this._lastBlockNumber) {
this.emit("didPoll", pollId);
return;
}
// First polling cycle, trigger a "block" events // First polling cycle, trigger a "block" events
if (this._emitted.block === -2) { if (this._emitted.block === -2) {
@ -439,7 +447,11 @@ export class BaseProvider extends Provider {
} }
get blockNumber(): number { get blockNumber(): number {
return this._fastBlockNumber; this._getInternalBlockNumber(100 + this.pollingInterval / 2).then((blockNumber) => {
this._setFastBlockNumber(blockNumber);
});
return (this._fastBlockNumber != null) ? this._fastBlockNumber: -1;
} }
get polling(): boolean { get polling(): boolean {
@ -447,16 +459,30 @@ export class BaseProvider extends Provider {
} }
set polling(value: boolean) { set polling(value: boolean) {
setTimeout(() => { if (value && !this._poller) {
if (value && !this._poller) { this._poller = setInterval(this.poll.bind(this), this.pollingInterval);
this._poller = setInterval(this.poll.bind(this), this.pollingInterval);
this.poll();
} else if (!value && this._poller) { if (!this._bootstrapPoll) {
clearInterval(this._poller); this._bootstrapPoll = setTimeout(() => {
this._poller = null; this.poll();
// We block additional polls until the polling interval
// is done, to prevent overwhelming the poll function
this._bootstrapPoll = setTimeout(() => {
// If polling was disabled, something may require a poke
// since starting the bootstrap poll and it was disabled
if (!this._poller) { this.poll(); }
// Clear out the bootstrap so we can do another
this._bootstrapPoll = null;
}, this.pollingInterval);
}, 0);
} }
}, 0);
} else if (!value && this._poller) {
clearInterval(this._poller);
this._poller = null;
}
} }
get pollingInterval(): number { get pollingInterval(): number {

@ -25,6 +25,10 @@ export interface OnceBlockable {
once(eventName: "block", handler: () => void): void; once(eventName: "block", handler: () => void): void;
} }
export interface OncePollable {
once(eventName: "poll", handler: () => void): void;
}
export type PollOptions = { export type PollOptions = {
timeout?: number, timeout?: number,
floor?: number, floor?: number,
@ -32,6 +36,7 @@ export type PollOptions = {
interval?: number, interval?: number,
retryLimit?: number, retryLimit?: number,
onceBlock?: OnceBlockable onceBlock?: OnceBlockable
oncePoll?: OncePollable
}; };
export type FetchJsonResponse = { export type FetchJsonResponse = {
@ -230,6 +235,9 @@ export function poll(func: () => Promise<any>, options?: PollOptions): Promise<a
if (result !== undefined) { if (result !== undefined) {
if (cancel()) { resolve(result); } if (cancel()) { resolve(result); }
} else if (options.oncePoll) {
options.oncePoll.once("poll", check);
} else if (options.onceBlock) { } else if (options.onceBlock) {
options.onceBlock.once("block", check); options.onceBlock.once("block", check);