为什么我的承诺未定义?

时间:2017-03-03 11:25:38

标签: javascript node.js ecmascript-6 promise

背景

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

出于本演示的目的,以下是异步函数,它采用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);

    });
};

目的

我的目标是拥有一个函数,该函数将采用url的{​​{1}}参数,然后每隔X ms调用一次,或直到不再有参数为止。

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

举个例子,假设我连续20次调用asyncMock。通常,这20个电话会立即完成。我想要的是,它确保在20个呼叫中的每个呼叫之间存在Xms的延迟。

暂定

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

asyncMock

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

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

问题

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

2 个答案:

答案 0 :(得分:5)

因为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传递网址,所以:

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);

答案 1 :(得分:1)

这是你的问题:

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

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