Bluebird-Collections

Promise实例方法和Promise类核心静态方法用于处理promise或者混合型(mixed)promise和值的集合。

所有的集合实例方法都等效于Promise对象中的静态方法,例如:somePromise.map(...)... 等效于 Promise.map(somePromise,…)…somePromise.all和Promise.all一样。

集合方法都不能够修改原来的输入。如果他们值定义了undefined,我们看做Holes in Array(holes in Array)。

Promise.all

Promise.all(Iterable<any>|Promise<Iterable<any>> input) –> Promise

当你等待多个Promise完成时候,这个方法非常有用。

给一个可迭代类型Iterable(数组是可迭代),或者一个可迭代的Promise可以产出Promise或者产出Promise和其他值混合体(注:Promise和其他类型值的混合体),Iterable成为数组一样遍历所有的值,而且,当所有的项(item)的Promise都为为fulfilled,Promise.all才会返回一个fulfilled的Promise。返回来的值是一个和原来数组一样的大小的值。如果有任何一个Promise为rejects在这个集合中。这个Promise集合将会返回是rejectsed。

var files = [];
for (var i = 0; i < 100; ++i) {
    files.push(fs.writeFileAsync("file-" + i + ".txt", "", "utf-8"));
}
Promise.all(files).then(function() {
    console.log("all the files were created");
});

这个方法和原生Promise中的Promise.all兼容。

Promise.props

Promise.props(Object|Map|Promise<Object|Map> input) -> Promise

和Promise.all一样,但是他的迭代值是对象类型或者Maps* entries(Map类实体),当所有的参数对象属性或者是Maps* Values 都是fulfilled条件满足Promise返回是fulfilled。Promise 的 fulfilled值是一个对象或者一个Map,fulfilled状态值各自keys对应原来对象和Map,如果任何对象中或者map中的Promise rejects了,然后整个Promise return rejected

如果Object参数是个可靠的Promise,然后它将会向对待对象作为一个Promise,而不是对象属性。所以其他的对象类型(除了Map),对待他们的属性同Object.Keys返回值-这个对象具有可列举的属性

*只有原生的ECMAScript 6 Map 实现类是被支持的。

**如果map的key值碰巧是一个Promise,他们不会去等待,并且最后结果集Map还是保持原来的Promise实例。

Promise.props({
    pictures: getPictures(),
    comments: getComments(),
    tweets: getTweets()
}).then(function(result) {
    console.log(result.tweets, result.pictures, result.comments);
});
var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs"));
var _ = require("lodash");
var path = require("path");
var util = require("util");

function directorySizeInfo(root) {
    var counts = {dirs: 0, files: 0};
    var stats = (function reader(root) {
        return fs.readdirAsync(root).map(function(fileName) {
            var filePath = path.join(root, fileName);
            return fs.statAsync(filePath).then(function(stat) {
                stat.filePath = filePath;
                if (stat.isDirectory()) {
                    counts.dirs++;
                    return reader(filePath)
                }
                counts.files++;
                return stat;
            });
        }).then(_.flatten);
    })(root).then(_);

    var smallest = stats.call("min", "size").call("pick", "size", "filePath").call("value");
    var largest = stats.call("max", "size").call("pick", "size", "filePath").call("value");
    var totalSize = stats.call("pluck", "size").call("reduce", function(a, b) {
        return a + b;
    }, 0);

    return Promise.props({
        counts: counts,
        smallest: smallest,
        largest: largest,
        totalSize: totalSize
    });
}


directorySizeInfo(process.argv[2] || ".").then(function(sizeInfo) {
    console.log(util.format("                                                

        %d directories, %d files                                             

        Total size: %d bytes                                                 

        Smallest file: %s with %d bytes                                      

        Largest file: %s with %d bytes                                       

    ", sizeInfo.counts.dirs, sizeInfo.counts.files, sizeInfo.totalSize,
        sizeInfo.smallest.filePath, sizeInfo.smallest.size,
        sizeInfo.largest.filePath, sizeInfo.largest.size));
});

注意:如果你不需要检索对象的属性时候,可以使用Promise.join:更方便一些

Promise.join(getPictures(), getComments(), getTweets(),
    function(pictures, comments, tweets) {
    console.log(pictures, comments, tweets);
});

Promise.any

Promise.any(Iterable<any>|Promise<Iterable<any>> input) -> Promise

Like Promise.some, with 1 as count. However, if the promise fulfills, the fulfillment value is not an array of 1 but the value directly.

Promise.some

Promise.some(
    Iterable<any>|Promise<Iterable<any>> input,
    int count
) -> Promise

给一个可迭代器(数组是可迭代器),或者是promise迭代器,一个可以产生出promise(或者primise和values掺和体),遍历迭代器中所有的值像迭代数组一样,如果迭代器中fulfilled个数有大于参数count话,则这个返回fulfilled,fulfilled values作为一个数组且根据fulfille完成前后顺序。

这个例子ping 4个网站,记录最快的两个在控制台上:

Promise.some([
    ping("ns1.example.com"),
    ping("ns2.example.com"),
    ping("ns3.example.com"),
    ping("ns4.example.com")
], 2).spread(function(first, second) {
    console.log(first, second);
});

如果太多的promise被拒绝(rejected)了,这个promise集合能从不变成fulfilled,他会马上rejected,且通过AggregateError按先后抛出错误原因。

你将从Promise.AggregateError得到一个AggregateError一个引用

Promise.some(...)
    .then(...)
    .then(...)
    .catch(Promise.AggregateError, function(err) {
        err.forEach(function(e) {
            console.error(e.stack);
        });
    });

Promise.map

Promise.map(
    Iterable<any>|Promise<Iterable<any>> input,
    function(any item, int index, int length) mapper,
    [Object {concurrency: int=Infinity} options]
) -> Promise

给一个可迭代器(数组是可迭代器),或者是promise迭代器,一个可以产生出promise(或者primise和values掺和体),遍历迭代器中所有的值像迭代数组一样,且使用参数中的mapper来 map the array to another .

mapper函数将会等待返回的promise而不会fulfilled直到所有mapped promises都fulfilled,才会返回Promises。如果在数组中任何一个promise rejectd,或者在mapper函数返回的promise是rejected,这返回值将会是rejected。

每一项的mapper函数尽可能都会被调用到,换而言之,when the promise for that item's index in the input array is fulfilled.他们的结果顺序不会是随机的,这.map以为这可以‘且同时性’条件,不像.all.

A common use of Promise.map is to replace the .push+Promise.all boilerplate:

var promises = [];
for (var i = 0; i < fileNames.length; ++i) {
    promises.push(fs.readFileAsync(fileNames[i]));
}
Promise.all(promises).then(function() {
    console.log("done");
});

// Using Promise.map:
Promise.map(fileNames, function(fileName) {
    // Promise.map awaits for returned promises as well.
    return fs.readFileAsync(fileName);
}).then(function() {
    console.log("done");
});

A more involved example:

var Promise = require("bluebird");
var join = Promise.join;
var fs = Promise.promisifyAll(require("fs"));
fs.readdirAsync(".").map(function(fileName) {
    var stat = fs.statAsync(fileName);
    var contents = fs.readFileAsync(fileName).catch(function ignore() {});
    return join(stat, contents, function(stat, contents) {
        return {
            stat: stat,
            fileName: fileName,
            contents: contents
        }
    });
// The return value of .map is a promise that is fulfilled with an array of the mapped values
// That means we only get here after all the files have been statted and their contents read
// into memory. If you need to do more operations per file, they should be chained in the map
// callback for concurrency.
}).call("sort", function(a, b) {
    return a.fileName.localeCompare(b.fileName);
}).each(function(file) {
    var contentLength = file.stat.isDirectory() ? "(directory)" : file.contents.length + " bytes";
    console.log(file.fileName + " last modified " + file.stat.mtime + " " + contentLength)
});
Map Option: concurrency

你也许需要制定并发限制

...map(..., {concurrency: 3});

The concurrency limit applies to Promises returned by the mapper function and it basically limits the number of Promises created. For example, if concurrency is3 and the mapper callback has been called enough so that there are three returned Promises currently pending, no further callbacks are called until one of the pending Promises resolves. So the mapper function will be called three times and it will be called again only after at least one of the Promises resolves.

Playing with the first example with and without limits, and seeing how it affects the duration when reading 20 files:

var Promise = require("bluebird");
var join = Promise.join;
var fs = Promise.promisifyAll(require("fs"));
var concurrency = parseFloat(process.argv[2] || "Infinity");
console.time("reading files");
fs.readdirAsync(".").map(function(fileName) {
    var stat = fs.statAsync(fileName);
    var contents = fs.readFileAsync(fileName).catch(function ignore() {});
    return join(stat, contents, function(stat, contents) {
        return {
            stat: stat,
            fileName: fileName,
            contents: contents
        }
    });
// The return value of .map is a promise that is fulfilled with an array of the mapped values
// That means we only get here after all the files have been statted and their contents read
// into memory. If you need to do more operations per file, they should be chained in the map
// callback for concurrency.
}, {concurrency: concurrency}).call("sort", function(a, b) {
    return a.fileName.localeCompare(b.fileName);
}).then(function() {
    console.timeEnd("reading files");
});
$ sync && echo 3 > /proc/sys/vm/drop_caches
$ node test.js 1
reading files 35ms
$ sync && echo 3 > /proc/sys/vm/drop_caches
$ node test.js Infinity
reading files: 9ms
The order map calls the mapper function on the array elements is not specified, there is no guarantee on the order in which it'll execute the maper on the elements. For order guarantee in sequential execution - see Promise.mapSeries.
原文地址:https://www.cnblogs.com/xiaopen/p/5657470.html