Merge branch '21128-toolbar-context-menu'
[arvados-workbench2.git] / src / common / use-async-interval.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import React from "react";
6
7 export const useAsyncInterval = function (callback, delay) {
8     const ref = React.useRef<{cb: () => Promise<any>, active: boolean}>({
9         cb: async () => {},
10         active: false}
11     );
12
13     // Remember the latest callback.
14     React.useEffect(() => {
15         ref.current.cb = callback;
16     }, [callback]);
17     // Set up the interval.
18     React.useEffect(() => {
19         function tick() {
20             if (ref.current.active) {
21                 // Wrap execution chain with promise so that execution errors or
22                 //   non-async callbacks still fall through to .finally, avoids breaking polling
23                 new Promise((resolve) => {
24                     return resolve(ref.current.cb());
25                 }).then(() => {
26                     // Promise succeeded
27                     // Possibly implement back-off reset
28                 }).catch(() => {
29                     // Promise rejected
30                     // Possibly implement back-off in the future
31                 }).finally(() => {
32                     setTimeout(tick, delay);
33                 });
34             }
35         }
36         if (delay !== null) {
37             ref.current.active = true;
38             setTimeout(tick, delay);
39         }
40         // Suppress warning about cleanup function - can be ignored when variables are unrelated to dom elements
41         //   https://github.com/facebook/react/issues/15841#issuecomment-500133759
42         // eslint-disable-next-line
43         return () => {ref.current.active = false;};
44     }, [delay]);
45 };