【问题标题】:Why is my promise returning undefined?为什么我的承诺返回未定义?
【发布时间】:2017-07-23 11:46:48
【问题描述】:

背景

我正在尝试创建一个将异步函数的执行延迟 X 毫秒的函数。

出于本演示的目的,以下是异步函数,它接受一个 URL:

/*
 *  This is a simulation of an async function. Be imaginative! 
 */
let asyncMock = function(url) {
    return new Promise(fulfil => {

        setTimeout(() => {
            fulfil({
                url,
                data: "banana"
            });
        }, 10000);

    });
};

目标

我的目标是有一个函数,它将接受 asyncMockurl 参数,然后每 X 毫秒调用一次,或者直到没有更多参数为止。

基本上,我希望asyncMock 的每次调用都用 X 毫秒分隔。

例如,假设我连续调用asyncMock 20 次。通常,这 20 个电话会立即完成。我想要的是确保 20 次通话之间有 Xms 的延迟。

暂定

我解决这个问题的想法是建立一个工厂,它将返回一个promise,它将在 X 毫秒后执行该函数。

let throttleFactory = function(args) {

    let {
        throttleMs
    } = args;

    let promise = Promise.resolve();

    let throttleAsync = function(url) {

        return promise.then(() => {

            setTimeout(anUrl => {
                return new Promise( fulfil => {
                    fulfil(asyncMock(anUrl));
                });
            }, throttleMs, url);
        });
    };

    return Object.freeze({
        throttleAsync
    });
};

理想情况下,我会像下面的示例一样使用这个工厂:

let throttleFuns = throttleFactory({
    throttleMs: 2000
});

console.log('running');

throttleFuns.throttleAsync('http://www.bananas.pt')
    .then(console.log)
    .catch(console.error);

throttleFuns.throttleAsync('http://www.fruits.es')
    .then(console.log)
    .catch(console.error);

throttleFuns.throttleAsync('http://www.veggies.com')
    .then(console.log)
    .catch(console.error);
// a ton of other calls in random places in code

问题

这里的问题是我的throttleAsync 函数立即输出了三遍undefined。我相信这可能是因为我没有正确定义promise

问题

如何修复此代码以按预期工作?

【问题讨论】:

  • 除了承诺问题之外,我看不出这是如何限制的。它会延迟事情,但不会扼杀它们。
  • 好的,也许我应该更改描述?我在上面。
  • 更新描述,感谢反馈!

标签: javascript node.js ecmascript-6 promise


【解决方案1】:

因为throttleAsync 返回调用promise.then 的结果,而then 回调不返回任何内容。这使得then 创建的承诺解析为undefined

您可能打算让它返回您正在创建的新承诺,但直到setTimeout 回调您才这样做。你想提前做(但还有更多,请继续阅读):

let throttleAsync = function(url) {

    return promise.then(() => {
        return new Promise( fulfil => {
            setTimeout(anUrl => {
                fulfil(asyncMock(anUrl));
            }, throttleMs, url);
        });
    });
};

也没有理由像这样通过setTimeout 传递 URL,所以:

let throttleAsync = function(url) {

    return promise.then(() => {
        return new Promise( fulfil => {
            setTimeout(() => {
                fulfil(asyncMock(url));
            }, throttleMs);
        });
    });
};

最初我虽然promise 是不必要的,但你已经澄清你想确保重复调用被throttleMs“隔开”。为此,我们将使用上述方法,但更新promise

let throttleAsync = function(url) {

    return promise = promise.then(() => {
    //     ^^^^^^^^^
        return new Promise( fulfil => {
            setTimeout(() => {
                fulfil(asyncMock(url));
            }, throttleMs);
        });
    });
};

这样,对asyncThrottle 的下一次调用将等到上一次调用触发后再开始下一次调用。

现场示例:

const throttleMs = 1000;

const asyncMock = url => url;

let promise = Promise.resolve();

let throttleAsync = function(url) {

    return promise = promise.then(() => {
    //     ^^^^^^^^^
        return new Promise( fulfil => {
            setTimeout(() => {
                fulfil(asyncMock(url));
            }, throttleMs);
        });
    });
};

console.log('running');

throttleAsync('http://www.bananas.pt')
    .then(console.log)
    .catch(console.error);

throttleAsync('http://www.fruits.es')
    .then(console.log)
    .catch(console.error);

throttleAsync('http://www.veggies.com')
    .then(console.log)
    .catch(console.error);

【讨论】:

  • 您的解决方案的问题是您同时拨打所有电话。我的目标是确保每个呼叫都由throttleMs 分隔。这就是为什么我尝试将所有内容都传递给同一个承诺。所以promise 对象只会在当前执行结束后运行下一次执行。在您的代码中,一切都会同时解决,这不是我计划实现的。我想要的是每个分辨率都用throttleMs 分隔。是不是更清楚了?不过感谢您的回复!
  • 这里的事情是我希望then 子句以throttleMs 延迟执行。这就是为什么我的setTimeout 在承诺之前:D
  • 根据您的反馈更新了描述,希望现在清楚了!
  • @Flame_Phoenix:如果你想让它们分开,上面的中间解决方案只需稍作改动即可;我会更新的。
  • 好的,这是为另一个问题而战。在我看来,最初的答案得到了很好的解释,亲爱的先生,你应该得到奖励。我会将其标记为已解决,并尝试在另一个问题中解释我的目标。如果我很幸运,你会看到它:P
【解决方案2】:

这是你的问题:

        setTimeout(anUrl => {
            return new Promise( fulfil => {
                fulfil(asyncMock(anUrl));
            });
        }, throttleMs, url);

您在这里所做的是从 setTimeout 回调返回一个承诺。 setTimeout 运行的函数的返回值被忽略,因此没有人会得到该值。

【讨论】:

  • 就是这样!如何修复它,使setTimeout处理的函数的返回值不被忽略?
  • @Flame_Phoenix 他们总是被忽略。而不是在 setTimeout 中返回一个新的承诺,你必须解决一个已经在其他地方返回的承诺,它不会被忽略。
猜你喜欢
  • 2021-01-28
  • 2015-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-27
  • 2018-09-10
相关资源
最近更新 更多