Promise 中的异步代码总是反模式吗?

Is async code in a Promise always an antipattern?

我从 看到它可以是 mix Promises 与 async 代码的反模式。

但是,这是否适用于所有情况

我找不到避免在以下代码中组合它们的简单方法:

此代码是否包含反模式?而且,如果是这样,我该如何在不引入内存泄漏的情况下进行补救?

参见第 10 行:new Promise( async (resolve) => {

——这看起来非常不合惯用,但我没有看到另一种方法来完成:将 await 语句包装在 while 循环中 本身 ,调度它,并返回一个句柄来中止循环。

var [setRepeatedTimeout, clearRepeatedTimeout] = (() => {
    const asleep = (delay) => new Promise(resolve => setTimeout(resolve, delay));
    const repeatedTimeoutIntervals = [];

    function setRepeatedTimeout(f, delay, ...arguments) {
        //Like setInterval, but waits for an invocation to complete before scheduling the next one
        //(Supports both classic and async functions)
        const mySemaphores = {notAborted: true};
        const intervalID = repeatedTimeoutIntervals.push(mySemaphores) - 1;
        new Promise( async (resolve) => {
            await asleep(delay);
            while(mySemaphores.notAborted) {
                await f(...arguments);
                await asleep(delay);
            }
            delete repeatedTimeoutIntervals[intervalID];
        });
        return intervalID;
    }

    function clearRepeatedTimeout(intervalID) {
        //Clears loops set by setInterval()
        repeatedTimeoutIntervals[intervalID].notAborted = false;
    }

    return [setRepeatedTimeout, clearRepeatedTimeout];
})();
<p><button onclick="(function createInterval(){
  const _ = {intervalID: undefined};
  _.intervalID = setRepeatedTimeout( () => {
    console.log(`Hello from intervalID ${_.intervalID}`)
  }, 2000)
})()">Create timer</button><br />
<form action="javascript:void(0);" onsubmit="(function clearInterval(intervalID){
  clearRepeatedTimeout(intervalID);
})(parseInt(event.target.elements.intervalID.value))">
<input name="intervalID" placeholder="intervalID"/><button type="submit">Clear timer</button></p>

另一个问题警告的问题,也可能是这里的问题,是如果传递给 Promise 构造函数的异步回调内部等待拒绝的东西,Promise 将 而不是拒绝。您当前的代码不会导致 f 永远拒绝,但是 setRepeatedTimeout 执行可能会拒绝的任务,您将得到未处理的拒绝和永久挂起:

var [setRepeatedTimeout, clearRepeatedTimeout] = (() => {
    const asleep = (delay) => new Promise(resolve => setTimeout(resolve, delay));
    const repeatedTimeoutIntervals = [];

    function setRepeatedTimeout(f, delay, ...arguments) {
        //Like setInterval, but waits for an invocation to complete before scheduling the next one
        //(Supports both classic and async functions)
        const mySemaphores = {notAborted: true};
        const intervalID = repeatedTimeoutIntervals.push(mySemaphores) - 1;
        new Promise( async (resolve) => {
            await asleep(delay);
            while(mySemaphores.notAborted) {
                await f(...arguments);
                await asleep(delay);
            }
            delete repeatedTimeoutIntervals[intervalID];
        });
        return intervalID;
    }

    function clearRepeatedTimeout(intervalID) {
        //Clears loops set by setInterval()
        repeatedTimeoutIntervals[intervalID].notAborted = false;
    }

    return [setRepeatedTimeout, clearRepeatedTimeout];
})();


const _ = { intervalID: undefined };
_.intervalID = setRepeatedTimeout(() => {
  console.log('Throwing...');
  return Promise.reject();
}, 2000)

如果您希望循环在遇到此类错误时继续,有一种方法可以在保持 async 的同时处理此类问题:只需捕获所有可能拒绝的内容(在 try /catch.catch):

var [setRepeatedTimeout, clearRepeatedTimeout] = (() => {
    const asleep = (delay) => new Promise(resolve => setTimeout(resolve, delay));
    const repeatedTimeoutIntervals = [];

    function setRepeatedTimeout(f, delay, ...arguments) {
        //Like setInterval, but waits for an invocation to complete before scheduling the next one
        //(Supports both classic and async functions)
        const mySemaphores = {notAborted: true};
        const intervalID = repeatedTimeoutIntervals.push(mySemaphores) - 1;
        new Promise( async (resolve) => {
            await asleep(delay);
            while(mySemaphores.notAborted) {
                await f(...arguments).catch(() => {}); // log error here if you want
                await asleep(delay);
            }
            delete repeatedTimeoutIntervals[intervalID];
        });
        return intervalID;
    }

    function clearRepeatedTimeout(intervalID) {
        //Clears loops set by setInterval()
        repeatedTimeoutIntervals[intervalID].notAborted = false;
    }

    return [setRepeatedTimeout, clearRepeatedTimeout];
})();


const _ = { intervalID: undefined };
_.intervalID = setRepeatedTimeout(() => {
  console.log('Throwing...');
  return Promise.reject();
}, 2000)

但是这里根本不需要 new Promise - 它永远不会解析,也永远不会被使用。只需使用异步 IIFE:

var [setRepeatedTimeout, clearRepeatedTimeout] = (() => {
    const asleep = (delay) => new Promise(resolve => setTimeout(resolve, delay));
    const repeatedTimeoutIntervals = [];

    function setRepeatedTimeout(f, delay, ...arguments) {
        //Like setInterval, but waits for an invocation to complete before scheduling the next one
        //(Supports both classic and async functions)
        const mySemaphores = {notAborted: true};
        const intervalID = repeatedTimeoutIntervals.push(mySemaphores) - 1;
        (async () => {
            await asleep(delay);
            while(mySemaphores.notAborted) {
                await f(...arguments).catch(() => {}); // log error here if you want
                await asleep(delay);
            }
            delete repeatedTimeoutIntervals[intervalID];
        })();
        return intervalID;
    }

    function clearRepeatedTimeout(intervalID) {
        //Clears loops set by setInterval()
        repeatedTimeoutIntervals[intervalID].notAborted = false;
    }

    return [setRepeatedTimeout, clearRepeatedTimeout];
})();


const _ = { intervalID: undefined };
_.intervalID = setRepeatedTimeout(() => {
  console.log('Throwing...');
  return Promise.reject();
}, 2000)

    (async () => {
        await asleep(delay);
        while(mySemaphores.notAborted) {
            await f(...arguments).catch(() => {}); // log error here if you want
            await asleep(delay);
        }
        delete repeatedTimeoutIntervals[intervalID];
    })();