为什么我的承诺未定义?

Fla*_*nix 2 javascript node.js promise ecmascript-6

背景

我试图创建一个延迟异步函数执行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);

    });
};
Run Code Online (Sandbox Code Playgroud)

目的

我的目标是拥有一个函数,它将接受url参数,asyncMock然后每隔X ms调用一次,或者直到没有剩下的参数为止.

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

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

试验

我想解决这个问题的方法是建立一个工厂,它将返回一个在X ms后执行该功能的承诺.

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
    });
};
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

问题

这里的问题是我的throttleAsync功能undefined立即输出三次.我相信这可能是因为我没有promise正确定义.

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

T.J*_*der 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);
        });
    });
};
Run Code Online (Sandbox Code Playgroud)

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

let throttleAsync = function(url) {

    return promise.then(() => {
        return new Promise( fulfil => {
            setTimeout(() => {
                fulfil(asyncMock(url));
            }, throttleMs);
        });
    });
};
Run Code Online (Sandbox Code Playgroud)

最初我虽然promise没有必要,但你已经澄清了你想确保重复的呼叫是"间隔"的throttleMs.为此,我们将使用上述内容,但更新promise:

let throttleAsync = function(url) {

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

这样,下一次调用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);
Run Code Online (Sandbox Code Playgroud)