//
// SPDX-License-Identifier: AGPL-3.0
-var react = require("react");
+import React from "react";
export const useAsyncInterval = function (callback, delay) {
- const savedCallback = react.useRef();
- const active = react.useRef(false);
+ const ref = React.useRef<{cb: () => Promise<any>, active: boolean}>({
+ cb: async () => {},
+ active: false}
+ );
// Remember the latest callback.
- react.useEffect(() => {
- savedCallback.current = callback;
+ React.useEffect(() => {
+ ref.current.cb = callback;
}, [callback]);
// Set up the interval.
- react.useEffect(() => {
- // useEffect doesn't like async callbacks (https://github.com/facebook/react/issues/14326) so create nested async callback
- (async () => {
- // Make tick() async
- async function tick() {
- if (active.current) {
- // If savedCallback is not set yet, no-op until it is
- savedCallback.current && await savedCallback.current();
+ React.useEffect(() => {
+ function tick() {
+ if (ref.current.active) {
+ // Wrap execution chain with promise so that execution errors or
+ // non-async callbacks still fall through to .finally, avoids breaking polling
+ new Promise((resolve) => {
+ return resolve(ref.current.cb());
+ }).then(() => {
+ // Promise succeeded
+ // Possibly implement back-off reset
+ }).catch(() => {
+ // Promise rejected
+ // Possibly implement back-off in the future
+ }).finally(() => {
setTimeout(tick, delay);
- }
+ });
}
- if (delay !== null) {
- active.current = true;
- setTimeout(tick, delay);
- }
- })(); // Call nested async function
- // We return the teardown function here since we can't from inside the nested async callback
- return () => {active.current = false;};
+ }
+ if (delay !== null) {
+ ref.current.active = true;
+ setTimeout(tick, delay);
+ }
+ // Suppress warning about cleanup function - can be ignored when variables are unrelated to dom elements
+ // https://github.com/facebook/react/issues/15841#issuecomment-500133759
+ // eslint-disable-next-line
+ return () => {ref.current.active = false;};
}, [delay]);
};