Старая поговорка гласит, что вы должны выбрать правильный инструмент для работы. ES6 обещает обеспечить основы. Если все, что вы когда-либо хотите или нуждаетесь, - это основы, тогда это должно / могло бы сработать для вас. Но в инструментальном ящике больше инструментов, чем просто основы, и бывают ситуации, когда эти дополнительные инструменты очень полезны. И я бы сказал, что в обещаниях ES6 даже отсутствуют некоторые основы, такие как обещание, которые полезны практически во всех проектах node.js.
Я наиболее знаком с библиотекой обещаний Bluebird, поэтому буду говорить в основном из своего опыта работы с этой библиотекой.
Итак, вот 6 моих главных причин использовать более продвинутую библиотеку Promise
Необещанные асинхронные интерфейсы - .promisify()
и .promisifyAll()
они невероятно полезны для обработки всех тех асинхронных интерфейсов, которые все еще требуют простых обратных вызовов и еще не возвращают обещания - одна строка кода создает обещанную версию всего интерфейса.
Быстрее - Bluebird значительно быстрее, чем обычные обещания в большинстве сред.
Последовательность итераций асинхронного массива - Promise.mapSeries()
или Promise.reduce()
позволяет выполнять итерацию по массиву, вызывая асинхронную операцию для каждого элемента, но упорядочивая асинхронные операции так, чтобы они выполнялись одна за другой, а не все одновременно. Вы можете сделать это либо потому, что это требуется целевому серверу, либо потому, что вам нужно передать один результат другому.
Polyfill - Если вы хотите использовать обещание в более старых версиях браузеров клиентов, вы будете нуждаться в polyfill в любом случае. С таким же успехом можно получить способный полифилл Так как node.js имеет обещания ES6, вам не нужно многозаполнение в node.js, но вы можете сделать это в браузере. Если вы кодируете как сервер, так и клиент node.js, может быть очень полезно иметь одну и ту же библиотеку обещаний и функции в обоих (проще делиться кодом, переключать контекст между средами, использовать общие методы кодирования для асинхронного кода и т. Д.). .).
Другие полезные функции - Bluebird имеет Promise.map()
, Promise.some()
, Promise.any()
, Promise.filter()
, Promise.each()
и Promise.props()
все из которых иногда удобно. В то время как эти операции могут быть выполнены с обещаниями ES6 и дополнительным кодом, Bluebird поставляется с этими операциями, уже подготовленными и предварительно протестированными, поэтому их проще и меньше кода использовать.
Встроенный в предупреждениях и 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
});
Альтернативой может быть ручное создание собственной оболочки обещаний для каждого fs
API, который вы хотите использовать:
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
});