Bluebird-Collections

Promise實例方法和Promise類核心靜態方法用於處理promise或者混合型(mixed)promise和值的集合。html

全部的集合實例方法都等效於Promise對象中的靜態方法,例如:somePromise.map(...)... 等效於 Promise.map(somePromise,…)…somePromise.all和Promise.all同樣。node

集合方法都不可以修改原來的輸入。若是他們值定義了undefined,咱們看作Holes in Array(holes in Array)。api

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。promise

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 rejectedapp

若是Object參數是個可靠的Promise,而後它將會向對待對象做爲一個Promise,而不是對象屬性。因此其餘的對象類型(除了Map),對待他們的屬性同Object.Keys返回值-這個對象具備可列舉的屬性函數

*只有原生的ECMAScript 6 Map 實現類是被支持的。網站

**若是map的key值碰巧是一個Promise,他們不會去等待,而且最後結果集Map仍是保持原來的Promise實例。ui

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("                                                \n\
        %d directories, %d files                                             \n\
        Total size: %d bytes                                                 \n\
        Smallest file: %s with %d bytes                                      \n\
        Largest file: %s with %d bytes                                       \n\
    ", 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.
本站公眾號
   歡迎關注本站公眾號,獲取更多信息