Vẫn còn lý do để sử dụng các thư viện hứa hẹn như Q hoặc BlueBird khi chúng ta có các lời hứa ES6? [đóng cửa]


228

Sau khi Node.js thêm hỗ trợ gốc cho các lời hứa, vẫn còn lý do để sử dụng các thư viện như Q hoặc BlueBird?

Ví dụ: nếu bạn đang bắt đầu một dự án mới và giả sử trong dự án này, bạn không có bất kỳ sự phụ thuộc nào sử dụng các thư viện này, chúng tôi có thể nói rằng thực sự không còn lý do nào để sử dụng các thư viện đó không?


4
lời hứa bản địa có những tính năng rất rất cơ bản. Các thư viện như Q hoặc Bluebird thêm một bó nữa. Nếu bạn cần những tính năng đó thì hãy sử dụng những thư viện đó.
gman

7
Tôi đã chỉnh sửa tiêu đề để làm cho nó ít hơn về "nhu cầu" và nhiều hơn về "lý do để vẫn sử dụng các thư viện hứa hẹn". Câu hỏi này có thể được trả lời bằng cách chủ yếu cung cấp sự thật, không phải ý kiến. Nó nên được mở lại bởi vì nó có thể được trả lời bằng cách cung cấp sự thật và không phải chủ yếu là ý kiến. Xem câu trả lời dưới đây như một minh chứng về điều đó.
jfriend00

11
@JaromandaX - Vui lòng xem xét mở lại ngay bây giờ rằng tiêu đề và câu hỏi đã được điều chỉnh để hiểu thêm về lý do tại sao người ta sẽ sử dụng thư viện lời hứa thay vì liệu "có cần" sử dụng thư viện lời hứa hay không. Theo tôi, câu hỏi này có thể được trả lời bằng cách cung cấp sự thật chứ không phải chủ yếu là ý kiến ​​- xem câu trả lời dưới đây là một minh chứng cho điều đó.
jfriend00

6
Câu hỏi này sau khi chỉnh sửa tiêu đề và câu trả lời được chấp nhận của nó, không dựa trên ý kiến.
tối đa

7
Đã đồng ý. Đây là một câu hỏi hoàn toàn hợp lệ trong hình thức hiện tại của nó. Tôi đã đề cử để mở lại.
Jules

Câu trả lời:


367

Câu ngạn ngữ cũ nói rằng bạn nên chọn công cụ phù hợp cho công việc. ES6 hứa hẹn cung cấp những điều cơ bản. Nếu tất cả những gì bạn muốn hoặc cần là những điều cơ bản, thì điều đó nên / có thể làm việc tốt cho bạn. Nhưng, có nhiều công cụ trong thùng công cụ hơn là những điều cơ bản và có những tình huống mà những công cụ bổ sung đó rất hữu ích. Và, tôi cho rằng các lời hứa ES6 thậm chí còn thiếu một số điều cơ bản như việc hứa hẹn rất hữu ích trong hầu hết mọi dự án của node.js.

Tôi quen thuộc nhất với thư viện lời hứa Bluebird vì vậy tôi sẽ nói hầu hết từ kinh nghiệm của tôi với thư viện đó.

Vì vậy, đây là 6 lý do hàng đầu của tôi để sử dụng thư viện Promise có khả năng hơn

  1. Các giao diện không đồng bộ không được quảng cáo - .promisify().promisifyAll()cực kỳ hữu ích để xử lý tất cả các giao diện không đồng bộ vẫn yêu cầu gọi lại đơn giản và không trả lại lời hứa - một dòng mã tạo ra phiên bản được hứa hẹn của toàn bộ giao diện.

  2. Nhanh hơn - Bluebird nhanh hơn đáng kể so với lời hứa bản địa trong hầu hết các môi trường.

  3. Trình tự lặp lại mảng async - Promise.mapSeries()hoặc Promise.reduce()cho phép bạn lặp qua một mảng, gọi một thao tác async trên mỗi phần tử, nhưng tuần tự các hoạt động async để chúng xảy ra lần lượt, không phải tất cả cùng một lúc. Bạn có thể làm điều này bởi vì máy chủ đích yêu cầu hoặc vì bạn cần truyền một kết quả cho lần tiếp theo.

  4. Polyfill - Nếu bạn muốn sử dụng lời hứa trong các phiên bản cũ hơn của ứng dụng khách trình duyệt, dù sao bạn cũng sẽ cần một polyfill. Cũng có thể có được một polyfill có khả năng. Vì node.js có các lời hứa ES6, bạn không cần polyfill trong node.js, nhưng bạn có thể trong trình duyệt. Nếu bạn đang mã hóa cả máy chủ và máy khách node.js, có thể rất hữu ích khi có cùng thư viện lời hứa và các tính năng trong cả hai (dễ chia sẻ mã, chuyển đổi ngữ cảnh giữa các môi trường, sử dụng các kỹ thuật mã hóa phổ biến cho mã async, v.v. .).

  5. Các tính năng hữu ích khác - Bluebird có Promise.map(), Promise.some(), Promise.any(), Promise.filter(), Promise.each()Promise.props()tất cả trong số đó là thỉnh thoảng tiện dụng. Mặc dù các hoạt động này có thể được thực hiện với các lời hứa ES6 và mã bổ sung, Bluebird đi kèm với các hoạt động này đã được xây dựng sẵn và thử nghiệm trước để sử dụng chúng đơn giản hơn và ít mã hơn.

  6. Được xây dựng trong Cảnh báo và Dấu vết ngăn xếp đầy đủ - Bluebird có một số cảnh báo được xây dựng để cảnh báo bạn về các vấn đề có thể là mã sai hoặc lỗi. Ví dụ: nếu bạn gọi một hàm tạo ra một lời hứa mới bên trong .then()trình xử lý mà không trả lại lời hứa đó (để liên kết nó với chuỗi lời hứa hiện tại), thì trong hầu hết các trường hợp, đó là một lỗi vô tình và Bluebird sẽ đưa ra cảnh báo cho bạn về điều đó hiệu ứng. Các cảnh báo Bluebird tích hợp khác được mô tả ở đây .

Dưới đây là một số chi tiết về các chủ đề khác nhau:

Hứa hẹn

Trong bất kỳ dự án node.js nào, tôi ngay lập tức sử dụng Bluebird ở mọi nơi vì tôi sử dụng .promisifyAll()rất nhiều trên các mô-đun node.js tiêu chuẩn như fsmô-đun.

Bản thân Node.js không cung cấp giao diện hứa hẹn cho các mô-đun tích hợp hoạt động không đồng bộ IO như fs mô-đun. Vì vậy, nếu bạn muốn sử dụng các lời hứa với các giao diện đó, bạn sẽ để lại mã cho trình bao bọc lời hứa xung quanh từng chức năng mô-đun bạn sử dụng hoặc nhận thư viện có thể thực hiện điều đó cho bạn hoặc không sử dụng lời hứa.

Bluebird Promise.promisify()Promise.promisifyAll()cung cấp gói tự động các API async quy ước gọi node.js để trả lại lời hứa. Nó cực kỳ hữu ích và tiết kiệm thời gian. Tôi sử dụng nó mọi lúc.

Đây là một ví dụ về cách thức hoạt động:

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));

fs.readFileAsync('somefile.text').then(function(data) {
   // do something with data here
});

Cách khác là tạo thủ công trình bao bọc lời hứa của riêng bạn cho mỗi fsAPI bạn muốn sử dụng:

const fs = require('fs');

function readFileAsync(file, options) {
    return new Promise(function(resolve, reject) {
        fs.readFile(file, options, function(err, data) {
            if (err) {
                reject(err);
            } else {
                 resolve(data);
            }
        });
    });
}

readFileAsync('somefile.text').then(function(data) {
   // do something with data here
});

Và, bạn phải tự làm điều này cho từng chức năng API bạn muốn sử dụng. Điều này rõ ràng không có ý nghĩa. Đó là mã soạn sẵn. Bạn cũng có thể có được một tiện ích làm việc này cho bạn. Bluebird Promise.promisify()Promise.promisifyAll()là một tiện ích như vậy.

Các tính năng hữu ích khác

Dưới đây là một số tính năng của Bluebird mà tôi đặc biệt thấy hữu ích (có một vài ví dụ mã bên dưới về cách chúng có thể lưu mã hoặc tăng tốc độ phát triển):

Promise.promisify()
Promise.promisifyAll()
Promise.map()
Promise.reduce()
Promise.mapSeries()
Promise.delay()

Ngoài chức năng hữu ích của nó, Promise.map()cũng hỗ trợ tùy chọn đồng thời cho phép bạn chỉ định số lượng hoạt động nên được phép chạy cùng lúc, điều này đặc biệt hữu ích khi bạn có nhiều việc phải làm, nhưng không thể lấn át một số hoạt động bên ngoài nguồn.

Một số trong số này có thể được gọi là độc lập và được sử dụng cho một lời hứa mà chính nó giải quyết thành một lần lặp có thể tiết kiệm rất nhiều mã.


Polyfill

Trong một dự án trình duyệt, vì bạn thường muốn vẫn hỗ trợ một số trình duyệt không có hỗ trợ Promise, cuối cùng bạn vẫn cần một polyfill. Nếu bạn cũng đang sử dụng jQuery, đôi khi bạn có thể chỉ sử dụng hỗ trợ lời hứa được tích hợp trong jQuery (mặc dù nó không chuẩn theo một số cách, có thể đã được sửa trong jQuery 3.0), nhưng nếu dự án liên quan đến bất kỳ hoạt động không đồng bộ hóa nào, tôi thấy các tính năng mở rộng trong Bluebird rất hữu ích.


Nhanh hơn

Cũng đáng chú ý rằng những lời hứa của Bluebird dường như nhanh hơn đáng kể so với những lời hứa được tích hợp trong động cơ V8. Xem bài đăng này để thảo luận thêm về chủ đề đó.


Một Node.js lớn đang thiếu

Điều khiến tôi cân nhắc sử dụng Bluebird ít hơn trong phát triển node.js sẽ là nếu node.js được xây dựng trong một hàm hứa hẹn để bạn có thể làm một cái gì đó như thế này:

const fs = requirep('fs');

fs.readFileAsync('somefile.text').then(function(data) {
   // do something with data here
});

Hoặc chỉ cung cấp các phương thức đã được hứa hẹn như là một phần của các mô-đun tích hợp.

Cho đến lúc đó, tôi làm điều này với Bluebird:

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));

fs.readFileAsync('somefile.text').then(function(data) {
   // do something with data here
});

Có vẻ hơi kỳ quặc khi có hỗ trợ lời hứa ES6 được tích hợp vào node.js và không có bất kỳ mô đun nào tích hợp trả lại lời hứa. Điều này cần được sắp xếp ra trong node.js. Cho đến lúc đó, tôi sử dụng Bluebird để quảng bá toàn bộ thư viện. Vì vậy, có vẻ như các lời hứa hiện được thực hiện khoảng 20% ​​trong tệp node.js vì không có mô-đun tích hợp nào cho phép bạn sử dụng lời hứa với chúng mà không cần gói thủ công trước.


Ví dụ

Đây là một ví dụ về Promise đơn giản so với lời hứa của Bluebird và Promise.map()để đọc một tập hợp các tệp song song và thông báo khi thực hiện với tất cả dữ liệu:

Lời hứa đồng bằng

const files = ["file1.txt", "fileA.txt", "fileB.txt"];
const fs = require('fs');

// make promise version of fs.readFile()
function fsReadFileP(file, options) {
    return new Promise(function(resolve, reject) {
        fs.readFile(file, options, function(err, data) {
            if (err) return reject(err);
            resolve(data);
        });
    });
}


Promise.all(files.map(fsReadFileP)).then(function(results) {
    // files data in results Array
}, function(err) {
    // error here
});

Bluebird Promise.map()Promise.promisifyAll()

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
const files = ["file1.txt", "fileA.txt", "fileB.txt"];

Promise.map(files, fs.readFileAsync).then(function(results) {
    // files data in results Array
}, function(err) {
    // error here
});

Đây là một ví dụ về Lời hứa đơn giản so với lời hứa của Bluebird và Promise.map()khi đọc một loạt URL từ máy chủ từ xa, nơi bạn có thể đọc nhiều nhất 4 lần, nhưng muốn giữ song song nhiều yêu cầu như được phép:

Lời hứa đồng bằng

const request = require('request');
const urls = [url1, url2, url3, url4, url5, ....];

// make promisified version of request.get()
function requestGetP(url) {
    return new Promise(function(resolve, reject) {
        request.get(url, function(err, data) {
            if (err) return reject(err);
            resolve(data);
        });
    });
}

function getURLs(urlArray, concurrentLimit) {
    var numInFlight = 0;
    var index = 0;
    var results = new Array(urlArray.length);
    return new Promise(function(resolve, reject) {
        function next() {
            // load more until concurrentLimit is reached or until we got to the last one
            while (numInFlight < concurrentLimit && index < urlArray.length) {
                (function(i) {
                    requestGetP(urlArray[index++]).then(function(data) {
                        --numInFlight;
                        results[i] = data;
                        next();
                    }, function(err) {
                        reject(err);
                    });
                    ++numInFlight;
                })(index);
            }
            // since we always call next() upon completion of a request, we can test here
            // to see if there was nothing left to do or finish
            if (numInFlight === 0 && index === urlArray.length) {
                resolve(results);
            }
        }
        next();
    });
}

Lời hứa của Bluebird

const Promise = require('bluebird');
const request = Promise.promisifyAll(require('request'));
const urls = [url1, url2, url3, url4, url5, ....];

Promise.map(urls, request.getAsync, {concurrency: 4}).then(function(results) {
    // urls fetched in order in results Array
}, function(err) {
    // error here
});

mặc dù nó cực kỳ không chuẩn theo một số cách - Họ cho rằng chúng là "Promise / A + tương thích" ngay bây giờ :) - blog.jquery.com/2016/01/14/jquery-3-0-beta-release
thefourtheye

1
@thefourtheye - Vâng, tôi biết họ đã và đang làm việc theo hướng tương thích Promise / A + trong 3.0. Nhưng, đó vẫn là bản beta. Nếu nó tuân theo lời hứa (ý định chơi chữ), nó có thể làm giảm một số lý do để sử dụng thư viện lời hứa bên ngoài trong trình duyệt JS nếu bạn đã sử dụng jQuery. Nó vẫn sẽ không có tất cả các tính năng hữu ích mà Bluebird làm và tôi sẽ vô cùng ngạc nhiên nếu nó phù hợp với hiệu suất của Bluebird vì vậy vẫn còn chỗ cho Bluebird bên cạnh một jQuery trong tương lai. Trong mọi trường hợp, câu hỏi của OP dường như chủ yếu là về node.js.
jfriend00

1
Có một lỗi đánh máy nhỏ trong mã ví dụ cuối cùng : return new Promise(function(resolve, rejct). Nên là:reject
Sebastian Muszyński

7
Node.js thực sự có util.promisifyngay bây giờ, mặc dù không có promisifyAlltương đương trực tiếp .
nyuszika7h

1
@Aurast - Vâng, v11 quan tâm fs, nhưng vẫn còn một số lý do khác để sử dụng Bluebird (yêu thích đặc biệt của tôi là concurrencytùy chọn Promise.map()) để tránh áp đảo dịch vụ mục tiêu mà bạn cần thực hiện một loạt các yêu cầu song song. Ngoài ra, vẫn còn rất nhiều giao diện không được quảng cáo khác để sử dụng Promisify ALL của Bluebird. Tuy nhiên, dần dần các lý do để lấy ngay Bluebird trong mọi dự án mới đang mờ dần khi bản thân node.js tăng cường hỗ trợ lời hứa tích hợp.
jfriend00
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.