O velho ditado diz que você deve escolher a ferramenta certa para o trabalho. As promessas do ES6 fornecem o básico. Se tudo o que você deseja ou precisa é o básico, isso deve / poderia funcionar bem para você. Porém, existem mais ferramentas na lixeira do que apenas o básico e há situações em que essas ferramentas adicionais são muito úteis. E, eu diria que as promessas do ES6 estão faltando alguns princípios básicos, como promisificação, que são úteis em praticamente todos os projetos node.js.
Eu estou mais familiarizado com a biblioteca de promessas do Bluebird, então falarei principalmente da minha experiência com essa biblioteca.
Então, aqui estão meus 6 principais motivos para usar uma biblioteca Promise mais capaz
Interfaces assíncronas não prometidas - .promisify()
e .promisifyAll()
são incrivelmente úteis para lidar com todas as interfaces assíncronas que ainda exigem retornos de chamada simples e ainda não retornam promessas - uma linha de código cria uma versão promissificada de uma interface inteira.
Mais rápido - o Bluebird é significativamente mais rápido do que as promessas nativas na maioria dos ambientes.
Seqüenciamento de iteração de matriz assíncrona - Promise.mapSeries()
ou Promise.reduce()
permite iterar através de uma matriz, chamando uma operação assíncrona em cada elemento, mas sequenciando as operações assíncronas para que elas ocorram uma após a outra, não todas ao mesmo tempo. Você pode fazer isso porque o servidor de destino exige ou porque precisa passar um resultado para o próximo.
Polyfill - Se você deseja usar promessas em versões mais antigas de clientes de navegador, precisará de um polyfill de qualquer maneira. Também pode obter um polyfill capaz. Como o node.js tem as promessas do ES6, você não precisa de um polyfill no node.js, mas em um navegador. Se você estiver codificando o servidor e o cliente node.js., pode ser muito útil ter a mesma biblioteca e recursos de promessa em ambos (mais fácil compartilhar código, alternar contexto entre ambientes, usar técnicas comuns de codificação para código assíncrono, etc.) .).
Outras funções úteis - Bluebird tem Promise.map()
, Promise.some()
, Promise.any()
, Promise.filter()
, Promise.each()
ePromise.props()
todos os que estão ocasionalmente útil. Embora essas operações possam ser executadas com as promessas do ES6 e código adicional, o Bluebird vem com essas operações já pré-construídas e pré-testadas, tornando mais simples e com menos código usá-las.
Avisos incorporados e rastreamentos de pilha completa - O Bluebird possui vários avisos incorporados que alertam sobre problemas que provavelmente são um código errado ou um bug. Por exemplo, se você chamar uma função que cria uma nova promessa dentro de um .then()
manipulador sem retornar a promessa (para vinculá-la à cadeia de promessas atual), na maioria dos casos, isso é um bug acidental e o Bluebird avisa sobre isso. efeito. Outros avisos Bluebird internos são descritos aqui .
Aqui estão mais alguns detalhes sobre esses vários tópicos:
PromisifyAll
Em qualquer projeto node.js., uso imediatamente o Bluebird em qualquer lugar, porque uso .promisifyAll()
muito nos módulos node.js. padrão, como o fs
módulo.
O Node.js não fornece uma interface de promessa aos módulos internos que executam E / S assíncrona como a fs
módulo. Portanto, se você deseja usar promessas com essas interfaces, você pode codificar manualmente um wrapper de promessa em torno de cada função do módulo que você usa ou obter uma biblioteca que pode fazer isso por você ou não usar promessas.
O Bluebird Promise.promisify()
e Promise.promisifyAll()
fornece um agrupamento automático de node.js. que chamam APIs assíncronas de convenção para retornar promessas. É extremamente útil e economiza tempo. Eu uso isso o tempo todo.
Aqui está um exemplo de como isso funciona:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
A alternativa seria criar manualmente seu próprio wrapper de promessa para cada fs
API que você deseja usar:
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
});
E você deve fazer isso manualmente para cada função da API que deseja usar. Isso claramente não faz sentido. É o código padrão. Você também pode obter um utilitário que faz esse trabalho para você. Bluebird Promise.promisify()
ePromise.promisifyAll()
são uma utilidade.
Outros recursos úteis
Aqui estão alguns dos recursos do Bluebird que eu acho especificamente úteis (há alguns exemplos de código abaixo sobre como eles podem salvar código ou acelerar o desenvolvimento):
Promise.promisify()
Promise.promisifyAll()
Promise.map()
Promise.reduce()
Promise.mapSeries()
Promise.delay()
Além de sua função útil, Promise.map()
também suporta uma opção de simultaneidade que permite especificar quantas operações devem ser executadas ao mesmo tempo, o que é particularmente útil quando você tem muito o que fazer, mas não pode sobrecarregar algumas pessoas externas. recurso.
Alguns deles podem ser chamados de autônomos e usados em uma promessa que se resolve como iterável, que pode economizar muito código.
Polyfill
Em um projeto de navegador, como você geralmente ainda deseja dar suporte a alguns navegadores que não têm suporte ao Promise, você acaba precisando de um polyfill de qualquer maneira. Se você também estiver usando o jQuery, às vezes poderá usar o suporte promissor embutido no jQuery (embora seja dolorosamente não-padrão em alguns aspectos, talvez corrigido no jQuery 3.0), mas se o projeto envolver alguma atividade assíncrona significativa, acho os recursos estendidos no Bluebird são muito úteis.
Mais rápido
Também vale notar que as promessas do Bluebird parecem ser significativamente mais rápidas do que as promessas incorporadas no V8. Veja este post para mais discussões sobre esse tópico.
Está faltando um grande Node.js
O que me faria pensar em usar menos o Bluebird no desenvolvimento do node.js. seria se o node.js. construísse uma função promisify para que você pudesse fazer algo assim:
const fs = requirep('fs');
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
Ou apenas ofereça métodos já prometidos como parte dos módulos internos.
Até então, faço isso com o Bluebird:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
Parece um pouco estranho ter o suporte à promessa do ES6 embutido no node.js e nenhum dos módulos internos retornar promessas. Isso precisa ser resolvido em node.js. Até então, eu uso o Bluebird para promisificar bibliotecas inteiras. Portanto, parece que as promessas estão implementadas em 20% no node.js agora, já que nenhum dos módulos integrados permite que você use promessas com eles sem quebrá-las manualmente primeiro.
Exemplos
Aqui está um exemplo de promessas simples vs. promisify do Bluebird e Promise.map()
para ler um conjunto de arquivos em paralelo e notificar quando concluído com todos os dados:
Promessas 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
});
Bluebird Promise.map()
ePromise.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
});
Aqui está um exemplo de promessas simples versus promisificação do Bluebird e Promise.map()
ao ler um monte de URLs de um host remoto onde você pode ler no máximo 4 por vez, mas deseja manter o máximo de solicitações em paralelo permitido:
JS Plain 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();
});
}
Promessas 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
});