diff --git a/CHANGELOG.md b/CHANGELOG.md index 03becc8..7d6bf59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,14 @@ # Changelog +## 4.0.0 WIP + +- Queue up multiple requested polls instead of rejecting them if an other poll is currently active + 💥 This may be a breaking change if you rely on the rejection to prevent multiple polls +- Added method `isPolling()` to get the current polling state + ## v3.1.0 2023-04-12 -- Fix issues if enableInterrupt() invoked multiple times with same or different gpio pin number. (thanks to Lyndel McGee [#55](https://github.com/crycode-de/node-pcf8574/issues/55), [#56](https://github.com/crycode-de/node-pcf8574/pull/56)) +- Fix issues if enableInterrupt() invoked multiple times with same or different gpio pin number. (thanks to Lyndel McGee [#55](https://github.com/crycode-de/node-pcf8574/issues/55), [#56](https://github.com/crycode-de/node-pcf8574/pull/56)) 💥 This may be a breaking change if you called `enabledInterrupt()` multiple times without calling `disableInterrupt()` first - wich was never intended! ## v3.0.1 2023-03-21 diff --git a/package-lock.json b/package-lock.json index 8e2c13a..8dbfc19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pcf8574", - "version": "3.1.0", + "version": "4.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "pcf8574", - "version": "3.1.0", + "version": "4.0.0", "license": "GPL-2.0", "dependencies": { "i2c-bus": "^5.2.3", diff --git a/package.json b/package.json index 8b583eb..59b3058 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pcf8574", - "version": "3.1.0", + "version": "4.0.0", "description": "Control each pin of a PCF8574/PCF8574A/PCF8575 I2C port expander IC.", "keywords": [ "pcf8574", diff --git a/src/pcf857x.ts b/src/pcf857x.ts index 6d0094e..610c373 100644 --- a/src/pcf857x.ts +++ b/src/pcf857x.ts @@ -11,6 +11,7 @@ import { I2CBus } from 'i2c-bus'; import { Gpio } from 'onoff'; import { PCF8574 } from './pcf8574'; import { PCF8575 } from './pcf8575'; +import { PromiseQueue } from './promise-queue'; /** * Enum of the known IC types. @@ -123,6 +124,9 @@ export abstract class PCF857x { /* nothing to do here */ }); + // enqueue a poll of current state and ignore any rejected promise + this._pollQueue.enqueue(() => this._poll()).catch(() => { /* nothing to do here */ }); } /** @@ -310,11 +314,11 @@ export abstract class PCF857x { - return this._poll(); + return this._pollQueue.enqueue(() => this._poll()); } /** @@ -391,6 +395,14 @@ export abstract class PCF857x { - return this._poll(pin); + return this._pollQueue.enqueue(() => this._poll(pin)); }); } diff --git a/src/promise-queue.ts b/src/promise-queue.ts new file mode 100644 index 0000000..1e91680 --- /dev/null +++ b/src/promise-queue.ts @@ -0,0 +1,90 @@ +/** + * Interface to describe a queued promise in a `PromiseQueue`. + */ +interface QueuedPromise { + promise: () => Promise; + resolve: (value: T) => void; + reject: (reason?: any) => void; +} + +/** + * A simple Promise Queue to allow the execution of some tasks in the correct order. + * + * (c) Peter Müller + */ +export class PromiseQueue { + + /** + * Queued Promises. + */ + private queue: QueuedPromise[] = []; + + /** + * Indicator that we are working on a Promise. + */ + private working: boolean = false; + + /** + * Enqueue a Promise. + * This adds the given Promise to the queue. If the queue was empty the Promise + * will be started immediately. + * @param promise Function which returns the Promise. + * @returns A Promise which will be resolves (or rejected) if the queued promise is done. + */ + public enqueue (promise: () => Promise): Promise { + return new Promise((resolve, reject) => { + this.queue.push({ + promise, + resolve, + reject, + }); + this.dequeue(); + }); + } + + /** + * Returns if the queue is empty and no more Promises are queued. + * @returns `true` if a Promise is active. + */ + public isEmpty(): boolean { + return !this.working && this.queue.length == 0; + } + + /** + * Get the first Promise of the queue and start it if there is no other + * Promise currently active. + * @returns `true` if Promise from the queue is started, `false` there is already an other active Promise or the queue is empty. + */ + private dequeue (): boolean { + if (this.working) { + return false; + } + + const item = this.queue.shift(); + if (!item) { + return false; + } + + try { + this.working = true; + item.promise() + .then((value) => { + item.resolve(value); + }) + .catch((err) => { + item.reject(err); + }) + .finally(() => { + this.working = false; + this.dequeue() + }); + + } catch (err) { + item.reject(err); + this.working = false; + this.dequeue(); + } + + return true; + } +}