循环任务瀑布 - 承诺蓝鸟

Joh*_*ell 7 javascript asynchronous node.js promise bluebird

我正在寻找蓝鸟的一些任务,只是使用超时作为实验机制.[不希望使用异步或任何其他库]

var Promise = require('bluebird');

var fileA = {
    1: 'one',
    2: 'two',
    3: 'three',
    4: 'four',
    5: 'five'
};


function calculate(key) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve(fileA[key]);
        }, 500);
    });
}

Promise.map(Object.keys(fileA), function (key) {
    calculate(key).then(function (res) {
        console.log(res);
    });
}).then(function () {
    console.log('finish');
});
Run Code Online (Sandbox Code Playgroud)

结果是

finish,
one,
two,
three,
four,
five,
Run Code Online (Sandbox Code Playgroud)

我需要循环只在每个超时完成后迭代一次,然后用完成后触发最后一个.

the*_*eye 15

  1. 在传递给的函数对象中Promise.map,需要返回一个Promise对象,以便解析所有的Promises,并将已解析的值数组传递给下一个then函数.在您的情况下,由于您没有明确地返回任何内容,undefined因此默认情况下将返回,而不是承诺.因此,使用s 解析了finish作为Promises执行的可执行函数.你可以这样确认Promises.mapundefined

    ...
    }).then(function (result) {
        console.log(result);
        console.log('finish');
    });
    
    Run Code Online (Sandbox Code Playgroud)

    会打印

    [ undefined, undefined, undefined, undefined, undefined ]
    finish
    one
    two
    three
    four
    five
    
    Run Code Online (Sandbox Code Playgroud)

    所以,你的代码应该有这样的return声明

    Promise.map(Object.keys(fileA), function (key) {
        return calculate(key).then(function (res) {
            console.log(res);
        });
    }).then(function () {
        console.log('finish');
    });
    
    Run Code Online (Sandbox Code Playgroud)

    现在,您将看到代码按顺序打印事物,因为我们返回Promise对象,并且在解析了finish所有Promise 之后调用thenable函数.但它们都没有顺序解决.如果发生这种情况,将在指定的时间过后打印每个数字.这将我们带到第二部分.

  2. Promise.map只要解析了数组中的Promises,就会执行作为参数传递的函数.引用文档,

    尽可能快地调用给定项的映射器函数,也就是说,当满足输入数组中该项的索引的承诺时.

    因此,数组中的所有值都将转换为Promises,并使用相应的值进行解析,并且将立即为每个值调用该函数.所以,他们都在同一时间等待500毫秒并立即解决.这不会顺序发生.

    由于您希望它们按顺序执行,因此您需要使用Promise.each.引用文档,

    迭代按顺序发生.....如果迭代器函数返回promise或者thenable,则在继续下一次迭代之前等待promise的结果.

    由于Promises是连续创建的并且在继续之前等待分辨率,因此保证了结果的顺序.所以你的代码应该成为

    Promise.each(Object.keys(fileA), function (key) {
        return calculate(key).then(function (res) {
            console.log(res);
        });
    }).then(function () {
        console.log('finish');
    });
    
    Run Code Online (Sandbox Code Playgroud)

    附加说明:

    如果命令无关紧要,正如Benjamin Gruenbaum所建议的那样,你可以使用Promise.map自己的concurrency限制,就像这样

    Promise.map(Object.keys(fileA), function (key) {
        return calculate(key).then(function (res) {
            console.log(res);
        });
    }, { concurrency: 1 }).then(function () {
        console.log('finish');
    });
    
    Run Code Online (Sandbox Code Playgroud)

    concurrency选项基本上限制了在创建更多承诺之前可以创建和解决的Promises数量.因此,在这种情况下,由于限制为1,它将创建第一个承诺,并且当达到限制时,它将等到创建的Promise结算,然后转到下一个Promise.


如果整个使用点calculate是引入延迟,那么我会建议Promise.delay,可以像这样使用

Promise.each(Object.keys(fileA), function (key) {
    return Promise.delay(500).then(function () {
        console.log(fileA[key]);
    });
}).then(function () {
    console.log('finish');
});
Run Code Online (Sandbox Code Playgroud)

delay 可以透明地将Promise的已解析值链接到下一个可执行的函数,因此代码可以缩短为

Promise.each(Object.keys(fileA), function (key) {
    return Promise.resolve(fileA[key]).delay(500).then(console.log);
}).then(function () {
    console.log('finish');
});
Run Code Online (Sandbox Code Playgroud)

由于Promise.delay接受动态值,您可以简单地写为

Promise.each(Object.keys(fileA), function (key) {
    return Promise.delay(fileA[key], 500).then(console.log);
}).then(function () {
    console.log('finish');
});
Run Code Online (Sandbox Code Playgroud)

如果Promise链在这里结束,那么最好使用.done()方法来标记它,就像这样

...
}).done(function () {
    console.log('finish');
});
Run Code Online (Sandbox Code Playgroud)

一般注意:如果您不打算在一个可执行的功能中进行任何处理,但是您只是为了跟踪进度或跟踪该过程,那么您可以更好地将它们更改为Promise.tap.所以,你的代码就会变成

Promise.each(Object.keys(fileA), function (key) {
    return Promise.delay(fileA[key], 500).tap(console.log);
}).then(function () {
    // Do processing
    console.log('finish');
});
Run Code Online (Sandbox Code Playgroud)