Есть ли еще причины использовать библиотеки обещаний, такие как Q или BlueBird, теперь, когда у нас есть обещания ES6? [закрыто]

229

После того, как Node.js добавил встроенную поддержку обещаний, есть ли еще причины использовать библиотеки, такие как Q или BlueBird?

Например, если вы начинаете новый проект, и давайте предположим, что в этом проекте у вас нет никаких зависимостей, которые используют эти библиотеки, можем ли мы сказать, что на самом деле больше нет причин использовать такие библиотеки?

Мурат Озгул
источник
4
родные обещания имеют очень очень основные черты. Такие библиотеки, как Q или Bluebird, добавляют еще больше. Если вам нужны эти функции, используйте эти библиотеки.
мужчина
7
Я отредактировал заголовок, чтобы сделать его менее «нужным» и больше «причинами использования библиотек обещаний». На этот вопрос можно ответить, прежде всего, предоставив факты, а не мнения. Его следует открыть, потому что на него можно ответить, предоставив факты, а не мнение. Посмотрите ответ ниже как демонстрацию этого.
jfriend00
11
@JaromandaX - Пожалуйста, подумайте о том, чтобы открыть заново, когда заголовок и вопрос были изменены, чтобы больше узнать о том, почему нужно использовать библиотеку обещаний, а не о том, «нужно ли» использовать библиотеку обещаний. На мой взгляд, на этот вопрос можно ответить, предоставив факты, а не мнение в первую очередь - см. Ответ ниже, чтобы продемонстрировать это.
jfriend00
6
Этот вопрос после редактирования названия и принятый ответ не основаны на мнении.
максимум
7
Согласовано. Это совершенно актуальный вопрос в его нынешнем виде. Я номинирован на повторное открытие.
Жюль

Ответы:

368

Старая поговорка гласит, что вы должны выбрать правильный инструмент для работы. ES6 обещает обеспечить основы. Если все, что вы когда-либо хотите или нуждаетесь, - это основы, тогда это должно / могло бы сработать для вас. Но в инструментальном ящике больше инструментов, чем просто основы, и бывают ситуации, когда эти дополнительные инструменты очень полезны. И я бы сказал, что в обещаниях ES6 даже отсутствуют некоторые основы, такие как обещание, которые полезны практически во всех проектах node.js.

Я наиболее знаком с библиотекой обещаний Bluebird, поэтому буду говорить в основном из своего опыта работы с этой библиотекой.

Итак, вот 6 моих главных причин использовать более продвинутую библиотеку Promise

  1. Необещанные асинхронные интерфейсы - .promisify()и .promisifyAll()они невероятно полезны для обработки всех тех асинхронных интерфейсов, которые все еще требуют простых обратных вызовов и еще не возвращают обещания - одна строка кода создает обещанную версию всего интерфейса.

  2. Быстрее - Bluebird значительно быстрее, чем обычные обещания в большинстве сред.

  3. Последовательность итераций асинхронного массива - Promise.mapSeries()или Promise.reduce()позволяет выполнять итерацию по массиву, вызывая асинхронную операцию для каждого элемента, но упорядочивая асинхронные операции так, чтобы они выполнялись одна за другой, а не все одновременно. Вы можете сделать это либо потому, что это требуется целевому серверу, либо потому, что вам нужно передать один результат другому.

  4. Polyfill - Если вы хотите использовать обещание в более старых версиях браузеров клиентов, вы будете нуждаться в polyfill в любом случае. С таким же успехом можно получить способный полифилл Так как node.js имеет обещания ES6, вам не нужно многозаполнение в node.js, но вы можете сделать это в браузере. Если вы кодируете как сервер, так и клиент node.js, может быть очень полезно иметь одну и ту же библиотеку обещаний и функции в обоих (проще делиться кодом, переключать контекст между средами, использовать общие методы кодирования для асинхронного кода и т. Д.). .).

  5. Другие полезные функции - Bluebird имеет Promise.map(), Promise.some(), Promise.any(), Promise.filter(), Promise.each()и Promise.props()все из которых иногда удобно. В то время как эти операции могут быть выполнены с обещаниями ES6 и дополнительным кодом, Bluebird поставляется с этими операциями, уже подготовленными и предварительно протестированными, поэтому их проще и меньше кода использовать.

  6. Встроенный в предупреждениях и Full Stack Traces - Bluebird имеет ряд встроенных предупреждений , что предупредить вас о вопросах, которые , вероятно , неправильный код или ошибка. Например, если вы вызываете функцию, которая создает новое обещание внутри .then()обработчика, не возвращая это обещание (чтобы связать его с текущей цепочкой обещаний), то в большинстве случаев это случайная ошибка, и Bluebird предупредит вас об этом. эффект. Другие встроенные предупреждения Bluebird описаны здесь .

Вот еще несколько подробностей по этим различным темам:

PromisifyAll

В любом проекте node.js я немедленно использую Bluebird везде, потому что я .promisifyAll()часто использую в стандартных модулях node.js, таких как fsмодуль.

Node.js сам по себе не предоставляет многообещающий интерфейс для встроенных модулей, которые выполняют асинхронный ввод-вывод подобно fs модулю. Таким образом, если вы хотите использовать обещания с этими интерфейсами, вам нужно либо вручную написать обертку обещаний вокруг каждой используемой вами функции модуля, либо получить библиотеку, которая может сделать это за вас или не использовать обещания.

Bluebird Promise.promisify()и Promise.promisifyAll()обеспечивают автоматическую упаковку асинхронных API-интерфейсов соглашения о вызовах node.js для возврата обещаний. Это чрезвычайно полезно и экономит время. Я использую это все время.

Вот пример того, как это работает:

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

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

Альтернативой может быть ручное создание собственной оболочки обещаний для каждого fsAPI, который вы хотите использовать:

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
});

И вы должны вручную сделать это для каждой функции API, которую вы хотите использовать. Это явно не имеет смысла. Это стандартный код. Вы можете также получить утилиту, которая сделает эту работу за вас. Блюберд Promise.promisify()и Promise.promisifyAll()такие утилиты.

Другие полезные функции

Вот некоторые из функций Bluebird, которые мне особенно полезны (ниже приведено несколько примеров кода о том, как они могут сохранить код или ускорить разработку):

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

В дополнение к своей полезной функции, Promise.map()также поддерживает параметр параллелизма, который позволяет вам указать, сколько операций должно быть разрешено запускать одновременно, что особенно полезно, когда у вас много дел, но вы не можете перегружать некоторые извне ресурс.

Некоторые из них можно назвать автономными и использовать в обещании, которое само по себе разрешается в итерируемое, что может сэкономить много кода.


Polyfill

В проекте браузера, поскольку вы, как правило, хотите поддерживать некоторые браузеры, которые не поддерживают Promise, вам все равно понадобится полифилл. Если вы также используете jQuery, иногда вы можете просто использовать поддержку обещаний, встроенную в jQuery (хотя она до некоторой степени нестандартна, возможно, исправлена ​​в jQuery 3.0), но если проект включает какие-либо существенные асинхронные действия, я нахожу расширенные функции в Bluebird очень полезны.


Быстрее

Также стоит отметить, что обещания Bluebird выглядят значительно быстрее, чем обещания, встроенные в V8. Смотрите этот пост для дальнейшего обсуждения этой темы.


Отсутствует большая вещь Node.js

То, что заставило бы меня задуматься об использовании Bluebird при разработке node.js, было бы, если бы node.js встроил функцию promisify, чтобы вы могли сделать что-то вроде этого:

const fs = requirep('fs');

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

Или просто предложите уже обещанные методы как часть встроенных модулей.

До этого я делаю это с Bluebird:

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

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

Кажется немного странным иметь встроенную поддержку обещаний ES6 в node.js, и ни один из встроенных модулей не возвращает обещания. Это должно быть отсортировано в node.js. До этого я использую Bluebird для обещания целых библиотек. Таким образом, создается впечатление, что обещания реализованы примерно на 20% в node.js, поскольку ни один из встроенных модулей не позволяет использовать обещания с ними без предварительной их переноса вручную.


Примеры

Вот пример простых обещаний против обещания Bluebird и Promise.map()для параллельного чтения набора файлов и уведомления о завершении работы со всеми данными:

Простые обещания

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
});

Синяя птица 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
});

Вот пример простых обещаний против обещания Bluebird и Promise.map()при чтении группы URL-адресов с удаленного хоста, где вы можете читать не более 4 за раз, но хотите поддерживать столько запросов параллельно, сколько это возможно:

Простой JS Promises

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();
    });
}

Обещания Синей птицы

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
});
jfriend00
источник
хотя в некоторых отношениях это ужасно нестандартно - они утверждают, что теперь они «совместимы с Promises / A +» :) - blog.jquery.com/2016/01/14/jquery-3-0-beta-released
thefourtheye
1
@thefourtheye - Да, я знаю, что они работают над совместимостью Promise / A + в 3.0. Но это все еще в бета-версии. Если оно соответствует обещанию (предназначено для каламбура), это может устранить некоторые причины использования внешней библиотеки обещаний в браузере JS, если вы уже использовали jQuery. Он по-прежнему не будет иметь всех полезных функций, которые есть в Bluebird, и я был бы крайне удивлен, если бы он соответствовал производительности Bluebird, так что в некоторых случаях есть еще место для Bluebird наряду с будущим jQuery. В любом случае вопрос ОП, по-видимому, в основном касается node.js.
Jfriend00
1
В последнем примере кода есть небольшая опечатка return new Promise(function(resolve, rejct). Должно быть:reject
Себастьян Мушински
7
Node.js на самом деле есть util.promisifyсейчас, хотя прямого promisifyAllэквивалента нет.
nyuszika7h
1
@Aurast - Да, v11 позаботился fs, но все же есть некоторые другие причины использовать Bluebird (мой любимый concurrencyвариант - вариант Promise.map()), чтобы не перегружать целевой сервис, к которому вам нужно сделать кучу параллельных запросов. Кроме того, все еще много других не обещанных интерфейсов, чтобы использовать promisifyAll от Bluebird. Но постепенно причины немедленного использования Bluebird в каждом новом проекте исчезают, так как сам node.js усиливает встроенную поддержку обещаний.
Jfriend00