Ainda existem razões para usar bibliotecas de promessas como Q ou BlueBird agora que temos promessas do ES6? [fechadas]

228

Depois que o Node.js adicionou suporte nativo a promessas, ainda existem motivos para usar bibliotecas como Q ou BlueBird?

Por exemplo, se você está iniciando um novo projeto e vamos assumir que neste projeto você não possui nenhuma dependência que utilize essas bibliotecas, podemos dizer que realmente não há mais motivos para usar essas bibliotecas?

Murat Ozgul
fonte
4
promessas nativas têm características muito, muito básicas. Bibliotecas como Q ou Bluebird adicionam muito mais. Se você precisar desses recursos, use essas bibliotecas.
gman
7
Editei o título para torná-lo menos sobre "necessidade" e mais sobre "razões para ainda usar bibliotecas de promessas". Esta pergunta pode ser respondida fornecendo principalmente fatos, não opiniões. Deve ser reaberto porque pode ser respondido fornecendo fatos e não principalmente opiniões. Veja a resposta abaixo como uma demonstração disso.
precisa saber é
11
@JaromandaX - Por favor, considere reabrir agora que o título e a pergunta foram aprimorados para saber mais sobre por que alguém usaria uma biblioteca de promessas, em vez de se "precisa" usar uma biblioteca de promessas. Na minha opinião, essa pergunta pode ser respondida fornecendo fatos e não principalmente a opinião - veja a resposta abaixo como uma demonstração disso.
precisa saber é
6
Esta pergunta após a edição do título e sua resposta aceita não são baseadas em opiniões.
Max
7
Acordado. Esta é uma pergunta perfeitamente válida em sua forma atual. Eu nomeei para reabrir.
Jules

Respostas:

367

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

  1. 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.

  2. Mais rápido - o Bluebird é significativamente mais rápido do que as promessas nativas na maioria dos ambientes.

  3. 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.

  4. 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.) .).

  5. 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.

  6. 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 fsmó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 fsAPI 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
});
jfriend00
fonte
embora isso seja dolorosamente não-padrão em alguns aspectos - Eles afirmam que são "Promessas / compatíveis com A +" agora :) - blog.jquery.com/2016/01/14/jquery-3-0-beta-released
thefourtheye
1
@thefourtheye - Sim, eu sei que eles têm trabalhado para a compatibilidade do Promise / A + no 3.0. Mas isso ainda está na versão beta. Se corresponder à promessa (trocadilho intencional), poderá evitar alguns dos motivos para usar uma biblioteca de promessa externa no JS do navegador se você já estiver usando o jQuery. Ele ainda não terá todos os recursos úteis que o Bluebird possui e eu ficaria extremamente surpreso se corresponder ao desempenho do Bluebird, para que ainda haja espaço para o Bluebird ao lado de um futuro jQuery em alguns casos. De qualquer forma, a pergunta do OP parece ser principalmente sobre node.js.
precisa saber é
1
Há um pequeno erro de digitação no último exemplo de código: return new Promise(function(resolve, rejct). Deveria ser: #reject
Sebastian Muszyński
7
O Node.js atualmente tem util.promisifyagora, embora não haja promisifyAllequivalente direto .
precisa saber é o seguinte
1
@Aurast - Sim, a v11 cuida fs, mas ainda existem outras razões para usar o Bluebird (o meu favorito em particular é a concurrencyopção Promise.map()) para não sobrecarregar um serviço de destino para o qual você precisa fazer várias solicitações paralelas. Além disso, ainda existem muitas outras interfaces não prometidas para usar o promisifyAll do Bluebird. Mas, lentamente, os motivos para procurar imediatamente o Bluebird em cada novo projeto estão desaparecendo à medida que o node.js reforça seu suporte interno promissor.
jfriend00