¿Existen todavía razones para utilizar bibliotecas de promesas como Q o BlueBird ahora que tenemos promesas de ES6? [cerrado]
Después de que Node.js agregó soporte nativo para promesas, ¿todavía hay razones para usar bibliotecas como Q o BlueBird?
Por ejemplo, si está iniciando un nuevo proyecto y supongamos que en este proyecto no tiene ninguna dependencia que use estas bibliotecas, ¿podemos decir que realmente no hay más razones para usar dichas bibliotecas?
Descargo de responsabilidad: esta información está desactualizada. El Bluebird Github Repo tiene la siguiente nota
Utilice promesas nativas en su lugar si es posible. Las promesas nativas se han mantenido estables en Node.js y los navegadores durante aproximadamente 6 años y han sido rápidas durante aproximadamente 3. Bluebird todavía ofrece algunos métodos de utilidad útiles y puede usarlos, pero considere primero las promesas nativas.
Esto es algo bueno, las personas que trabajan en Bluebird y en las promesas han podido ayudar a incorporar la mayoría de las cosas útiles de Bluebird en el propio JavaScript y en las plataformas/motores. Todavía faltan cosas (¡.map/.filter están en camino con la propuesta de ayudas de iteración y los iteradores asíncronos!).
Si hay una característica que te mantiene usando bluebird. Háganos saber para que podamos intentar actualizarlo :)
Actualmente, solo se recomienda usar Bluebird si necesita admitir navegadores antiguos o EoL Node.js o como paso intermedio para usar advertencias/monitoreo para encontrar errores.
El viejo refrán dice que debes elegir la herramienta adecuada para el trabajo. Las promesas de ES6 proporcionan los conceptos básicos. Si todo lo que siempre quiere o necesita es lo básico, entonces eso debería o podría funcionar bien para usted. Pero hay más herramientas en la caja de herramientas además de las básicas y hay situaciones en las que esas herramientas adicionales son muy útiles. Y yo diría que a las promesas de ES6 incluso les faltan algunos de los conceptos básicos como la promisificación que son útiles en casi todos los proyectos de Node.js.
Estoy más familiarizado con la biblioteca de promesas de Bluebird , por lo que hablaré principalmente desde mi experiencia con esa biblioteca.
Entonces, aquí están mis 6 razones principales para usar una biblioteca Promise más capaz
Interfaces asíncronas no prometidas ,
.promisify()
y.promisifyAll()
son increíblemente útiles para manejar todas esas interfaces asíncronas que aún requieren devoluciones de llamadas simples y aún no devuelven promesas: una línea de código crea una versión prometida de una interfaz completa.Más rápido : Bluebird es significativamente más rápido que las promesas nativas en la mayoría de los entornos.
Secuenciación de la iteración de una matriz asíncrona ,
Promise.mapSeries()
oPromise.reduce()
le permite iterar a través de una matriz, llamando a una operación asíncrona en cada elemento, pero secuenciando las operaciones asíncronas para que sucedan una tras otra, no todas al mismo tiempo. Puedes hacer esto porque el servidor de destino lo requiere o porque necesitas pasar un resultado al siguiente.Polyfill : si desea utilizar promesas en versiones anteriores de clientes de navegador, necesitará un Polyfill de todos modos. También puede obtener un polirelleno capaz. Dado que node.js tiene promesas de ES6, no necesita un polyfill en node.js, pero sí puede hacerlo en un navegador. Si está codificando tanto el servidor como el cliente node.js, puede ser muy útil tener la misma biblioteca de promesas y características en ambos (más fácil de compartir código, cambio de contexto entre entornos, uso de técnicas de codificación comunes para código asíncrono, etc.). .).
Otras funciones útiles : Bluebird tiene
Promise.map()
,Promise.some()
,Promise.any()
, y todas ellas son útiles en ocasionesPromise.filter()
. Si bien estas operaciones se pueden realizar con promesas de ES6 y código adicional, Bluebird viene con estas operaciones ya prediseñadas y probadas, por lo que es más simple y requiere menos código usarlas.Promise.each()
Promise.props()
Advertencias integradas y seguimientos de pila completa : Bluebird tiene una serie de advertencias integradas que le alertan sobre problemas que probablemente sean un código incorrecto o un error. Por ejemplo, si llama a una función que crea una nueva promesa dentro de un
.then()
controlador sin devolver esa promesa (para vincularla a la cadena de promesa actual), en la mayoría de los casos, se trata de un error accidental y Bluebird le dará una advertencia. efecto. Otras advertencias integradas de Bluebird se describen aquí .
Aquí hay más detalles sobre estos diversos temas:
Promisificar todo
En cualquier proyecto de node.js, uso inmediatamente Bluebird en todas partes porque uso .promisifyAll()
mucho en módulos estándar de node.js como el fs
módulo.
Node.js en sí no proporciona una interfaz prometedora para los módulos integrados que realizan E/S asíncronas como el fs
módulo. Entonces, si desea usar promesas con esas interfaces, debe codificar manualmente un contenedor de promesa alrededor de cada función de módulo que use u obtener una biblioteca que pueda hacer eso por usted o no usar promesas.
Bluebird's Promise.promisify()
y Promise.promisifyAll()
proporciona un ajuste automático de node.js que llama a las API asíncronas de convención para devolver promesas. Es extremadamente útil y ahorra tiempo. Lo uso todo el tiempo.
Aquí hay un ejemplo de cómo funciona:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
La alternativa sería crear manualmente su propio contenedor de promesa para cada fs
API que desee utilizar:
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
});
Y debe hacer esto manualmente para cada función API que desee utilizar. Esto claramente no tiene sentido. Es un código repetitivo. También podrías conseguir una utilidad que haga este trabajo por ti. Los de Bluebird Promise.promisify()
y Promise.promisifyAll()
son de gran utilidad.
Otras funciones útiles
Estas son algunas de las características de Bluebird que encuentro específicamente útiles (hay un par de ejemplos de código a continuación sobre cómo pueden guardar código o acelerar el desarrollo):
Promise.promisify()
Promise.promisifyAll()
Promise.map()
Promise.reduce()
Promise.mapSeries()
Promise.delay()
Además de su útil función, Promise.map()
también admite una opción de concurrencia que le permite especificar cuántas operaciones se deben permitir ejecutar al mismo tiempo, lo cual es particularmente útil cuando tiene mucho que hacer, pero no puede abrumar a algunas externas. recurso.
Algunos de estos pueden llamarse independientes y usarse en una promesa que a su vez se resuelve en un iterable que puede ahorrar una gran cantidad de código.
Polirelleno
En un proyecto de navegador, dado que generalmente desea seguir admitiendo algunos navegadores que no son compatibles con Promise, terminará necesitando un polyfill de todos modos. Si también estás usando jQuery, a veces puedes simplemente usar el soporte de promesa integrado en jQuery (aunque es dolorosamente no estándar en algunos aspectos, tal vez arreglado en jQuery 3.0), pero si el proyecto involucra alguna actividad asíncrona significativa, encuentro Las funciones ampliadas de Bluebird son muy útiles.
Más rápido
También vale la pena señalar que las promesas de Bluebird parecen ser significativamente más rápidas que las promesas integradas en V8. Consulte esta publicación para obtener más información sobre ese tema.
Una gran cosa que falta Node.js
Lo que me haría considerar usar menos Bluebird en el desarrollo de node.js sería si node.js construyera una función promisify para que pudieras hacer algo como esto:
const fs = requirep('fs');
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
O simplemente ofrecer métodos ya prometidos como parte de los módulos integrados.
Hasta entonces, hago esto con Bluebird:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
Parece un poco extraño tener soporte de promesas de ES6 integrado en node.js y que ninguno de los módulos integrados devuelva promesas. Esto debe solucionarse en node.js. Hasta entonces, uso Bluebird para prometer bibliotecas enteras. Entonces, parece que las promesas ahora están implementadas en aproximadamente un 20% en node.js, ya que ninguno de los módulos integrados le permite usar promesas con ellas sin envolverlas manualmente primero.
Ejemplos
Aquí hay un ejemplo de Promesas simples frente a la promesa de Bluebird y Promise.map()
para leer un conjunto de archivos en paralelo y notificar cuando haya terminado con todos los datos:
Promesas simples
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
});
pájaro azul Promise.map()
yPromise.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
});
Aquí hay un ejemplo de promesas simples frente a la promesa de Bluebird y Promise.map()
cuando se lee un montón de URL desde un host remoto donde puede leer como máximo 4 a la vez, pero desea mantener tantas solicitudes en paralelo como se permita:
Promesas simples de JS
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();
});
}
Promesas del pájaro azul
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
});