首页 > 解决方案 > Promise 中的异步代码总是反模式吗?

问题描述

我从这个问题中看到,将s 与代码混合 可能是一种反模式。Promiseasync

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

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

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

具体参见第 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>

标签: javascriptes6-promiseanti-patternsnon-recursive

解决方案


另一个问题警告的问题是,如果传递给 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或 with 中.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];
    })();

推荐阅读