From befe70ea00a930634866afeb9c2bcd6884114098 Mon Sep 17 00:00:00 2001
From: Christoph Reiter <reiter.christoph@gmail.com>
Date: Tue, 11 Feb 2020 11:28:57 +0100
Subject: [PATCH] Add AbortController related helpers

These allow linking multiple AbortControllers and ceate a timeout abort signal.

For example in case you want to abort a fetch in case

(1) the UI element gets removed
(2) a newer request replacing this one gets started
(3) a timeout happens because the fetch takes too long

createLinkedAbortController() allows merging multiple signals into one
and createTimeoutAbortSignal() allows creating a singal that auto aborts
after some time passes.
---
 packages/common/index.js              |  5 ++--
 packages/common/src/abort.js          | 37 +++++++++++++++++++++++++++
 packages/common/{ => src}/eventbus.js |  0
 packages/common/test/abort.js         | 20 +++++++++++++++
 packages/common/test/eventbus.js      |  2 +-
 5 files changed, 61 insertions(+), 3 deletions(-)
 create mode 100644 packages/common/src/abort.js
 rename packages/common/{ => src}/eventbus.js (100%)
 create mode 100644 packages/common/test/abort.js

diff --git a/packages/common/index.js b/packages/common/index.js
index cb63e2ac..a562cd77 100644
--- a/packages/common/index.js
+++ b/packages/common/index.js
@@ -1,3 +1,4 @@
-import {EventBus} from './eventbus.js';
+import {EventBus} from './src/eventbus.js';
+import {createLinkedAbortController, createTimeoutAbortSignal} from './src/abort.js';
 
-export {EventBus};
\ No newline at end of file
+export {EventBus, createLinkedAbortController, createTimeoutAbortSignal};
\ No newline at end of file
diff --git a/packages/common/src/abort.js b/packages/common/src/abort.js
new file mode 100644
index 00000000..33d59928
--- /dev/null
+++ b/packages/common/src/abort.js
@@ -0,0 +1,37 @@
+/**
+ * Takes multiple AbortSignal instances and returns a new AbortController which
+ * gets aborted if any of the AbortSignals do.
+ *
+ * @param  {...AbortSignal} signals
+ * @returns {AbortController}
+ */
+export function createLinkedAbortController(...signals) {
+    const controller = new AbortController();
+
+    for (const signal of signals) {
+        if (signal.aborted) {
+            controller.abort();
+            break;
+        } else {
+            signal.addEventListener('abort', () => {
+                controller.abort();
+            });
+        }
+    }
+
+    return controller;
+}
+
+/**
+ * Returns an AbortSignal which aborts after the specified time.
+ *
+ * @param {number} delay Delay in milliseconds
+ * @returns {AbortSignal}
+ */
+export function createTimeoutAbortSignal(delay) {
+    const controller = new AbortController();
+
+    setTimeout(() => {controller.abort(); }, delay);
+
+    return controller.signal;
+}
\ No newline at end of file
diff --git a/packages/common/eventbus.js b/packages/common/src/eventbus.js
similarity index 100%
rename from packages/common/eventbus.js
rename to packages/common/src/eventbus.js
diff --git a/packages/common/test/abort.js b/packages/common/test/abort.js
new file mode 100644
index 00000000..9c0af4dc
--- /dev/null
+++ b/packages/common/test/abort.js
@@ -0,0 +1,20 @@
+import {assert} from 'chai';
+import {createLinkedAbortController, createTimeoutAbortSignal} from '../src/abort.js';
+
+suite('abort', () => {
+    test('createLinkedAbortController', () => {
+        let c1 = new AbortController();
+        let c2 = new AbortController();
+        const linked = createLinkedAbortController(c1.signal, c2.signal);
+        assert.isFalse(linked.signal.aborted);
+        c1.abort();
+        assert.isTrue(linked.signal.aborted);
+        c1.abort();
+        linked.abort();
+    });
+
+    test('createTimeoutAbortSignal', () => {
+        const signal = createTimeoutAbortSignal(10000000);
+        assert.isFalse(signal.aborted);
+    });
+});
\ No newline at end of file
diff --git a/packages/common/test/eventbus.js b/packages/common/test/eventbus.js
index cf6d9403..209ff165 100644
--- a/packages/common/test/eventbus.js
+++ b/packages/common/test/eventbus.js
@@ -1,5 +1,5 @@
 import {assert} from 'chai';
-import {EventBus, createEventName, checkIndentifier} from '../eventbus.js';
+import {EventBus, createEventName, checkIndentifier} from '../src/eventbus.js';
 
 suite('helpers', () => {
     test('createEventName', () => {
-- 
GitLab