为什么我的Promise返回undefined?

4

背景

我正在尝试创建一个函数,将异步函数的执行延迟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 毫秒。

例如,假设我连续调用了 20 次 asyncMock。通常情况下,这些 20 次调用会立即执行。但我想要的是,在这 20 次调用之间每个调用之间都有 Xms 的延迟。

设想

我的解决思路是创建一个工厂,返回一个promise,该 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

问题

我该如何修改此代码以使其按预期工作?


1
除了 Promise 的问题之外,我不明白这个东西如何 限流。它会延迟事情的处理,但不会限制其处理速度。 - T.J. Crowder
好的,也许我应该改变描述吗?我正在处理。 - Flame_Phoenix
更新的描述,感谢反馈! - Flame_Phoenix
2个回答

9
因为throttleAsync返回调用promise.then的结果,而then回调没有返回任何内容。这使得then创建的promise以undefined的值解决。
你可能想要让它返回你正在创建的新Promise,但你直到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。因此,promise 对象只会在当前执行结束后运行下一个执行。在你的代码中,所有内容都会同时解析,这不是我想要实现的。我希望每个解析都由 throttleMs 分隔开。更清楚了吗?感谢您的回复! - Flame_Phoenix
这里的问题是我希望then子句在throttleMs延迟后执行。这就是为什么我的setTimeout在promise之前的原因:D - Flame_Phoenix
根据您的反馈更新了描述,希望现在更加清晰明了! - Flame_Phoenix
@Flame_Phoenix:如果你想让它们间隔开,上面的中间解决方案可以通过一个小改变来实现;我会更新。 - T.J. Crowder
1
好吧,这个问题可以留给其他人来解决。我认为原来的回答已经很好地解释了,并且,亲爱的先生,你应该得到奖励。我会将此标记为已解决,并试图在另一个问题中解释我的目标。如果我幸运的话,你会看到它的 :P - Flame_Phoenix
显示剩余9条评论

2
这是您的问题:

以下是问题内容:

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

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


这就是问题所在!我该如何修复它,以便由 setTimeout 处理的函数的返回值不被忽略? - Flame_Phoenix
@Flame_Phoenix 他们总是被忽略。在 setTimeout 中,你必须解决一个已经在其他地方返回的承诺,而不是返回一个新的承诺。 - rsp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接