Eric Elliott

Siga

Jan 23, 2017 · 11 min de leitura

Foto Kabun (CC BY NC SA 2.,0)

“Mestre, o JavaScript Entrevista” é uma série de posts projetado para preparar os candidatos para as perguntas mais comuns que são susceptíveis de ocorrer quando aplicar para um meados de nível sênior JavaScript posição. Estas são perguntas que uso frequentemente em entrevistas reais.

Uma promessa é um objeto que pode produzir um único valor algum momento no futuro: um valor resolvido, ou uma razão que não é resolvido (por exemplo, ocorreu um erro de rede)., Uma promessa pode estar em um dos três estados possíveis: cumprido, rejeitado ou pendente. Promise users can attach callbacks to handle the fulfilled value or the reason for rejection.

As promessas são ansiosas, o que significa que uma promessa vai começar a fazer qualquer tarefa que você lhe dê, assim que o construtor da promessa é invocado. Se você precisa de preguiça, confira observáveis ou tarefas.

Uma História Incompleta de Promessas

os Primeiros implementações de promessas e futuros (similar / relacionados idéia) começaram a aparecer em linguagens como MultiLisp e Concurrent Prolog já no início da década de 1980., O uso da palavra “promessa” foi cunhado por Barbara Liskov e Liuba Shrira em 1988.

A primeira vez que ouvi sobre promessas em JavaScript, Node era novinho em folha e a comunidade estava discutindo a melhor maneira de lidar com o comportamento assíncrono. A comunidade experimentou com promessas por um tempo, mas eventualmente se estabeleceu no nó-padrão erro-primeiros callbacks.

aproximadamente na mesma época, Dojo adicionou promessas através da API diferida. O crescente interesse e atividade eventualmente levou às promessas recém-formadas/uma especificação projetada para tornar várias promessas mais interoperáveis.,os comportamentos async da jQuery foram refactorados em torno de promessas. o apoio prometedor de jQuery tinha semelhanças notáveis com o diferido de Dojo, e rapidamente se tornou a implementação de promessas mais comumente usada em JavaScript devido à imensa popularidade de jQuery — por um tempo. No entanto, ele não suportou o comportamento de encadeamento de dois canais (cumprido/rejeitado) & gerenciamento de exceção que as pessoas estavam contando para construir ferramentas em cima das promessas.,

apesar dessas fraquezas, jQuery oficialmente fez promessas JavaScript mainstream, e Melhor stand-alone promise bibliotecas como Q, Quando, E Bluebird tornou-se muito popular. as incompatibilidades de implementação da jQuery motivaram alguns esclarecimentos importantes no promise spec, que foi reescrito e renomeado como Promises/a+ specification.,

ES6 trouxe um Promises / a + Compatível Promise global, e algumas API muito importantes foram construídas em cima do novo suporte padrão de Promessa: notavelmente o WHATWG Fetch spec e o padrão de funções Async (um rascunho de Fase 3 no momento da escrita).

As promessas aqui descritas são aquelas que são compatíveis com a especificação Promises/a+, com um foco no padrão ECMAScript Promise implementação.

How Promises Work

a promise is an object which can be returned synchronously from an asynchronous function., Será em um dos 3 estados possíveis:

uma promessa é liquidada se não estiver pendente (foi resolvida ou rejeitada). Às vezes as pessoas usam resolvido e resolvido para significar a mesma coisa: não pendente.uma vez estabelecida, uma promessa não pode ser reassentada. Calling resolve() or reject() again will have no effect. A imutabilidade de uma promessa estabelecida é uma característica importante.as promessas nativas de JavaScript não expõem Estados prometedores. Em vez disso, espera-se que trate a promessa como uma caixa negra., Somente a função responsável pela criação da promessa terá conhecimento do status da promessa, ou acesso para resolver ou rejeitar.

Aqui está uma função que devolve uma promessa que vai resolver, depois de um determinado tempo de atraso:

aguarde — promessa exemplo no CodePen

Nossa wait(3000) chamada vai esperar 3000ms (3 segundos) e, em seguida, faça 'Hello!'., Todas as promessas compatíveis com spec definem um .then() método que você usa para passar os manipuladores que podem levar o valor Resolvido ou rejeitado.

O Construtor de promessas ES6 assume uma função. Essa função leva dois parâmetros, resolve(), e reject(). No exemplo acima, estamos apenas usando resolve(), então eu deixei reject() fora da lista de parâmetros. Então chamamos setTimeout() para criar o atraso, e chamar resolve() quando está terminado.,

Você pode, opcionalmente, resolve() ou reject() com valores, que serão passados para as funções de retorno de chamada ligado com .then().

When ireject() with a value, I always pass anError object. Geralmente eu quero dois possíveis estados de resolução: o caminho feliz normal, ou uma exceção — qualquer coisa que impeça o caminho feliz normal de acontecer. Passing an Error object makes that explicit.,

importantes regras de Promessa

um padrão para promessas foi definido pela comunidade de Promessas/A+ especificação. Existem muitas implementações que estão em conformidade com o padrão, incluindo o JavaScript standard ECMAScript promises.

Promete seguir a especificação deve seguir um conjunto de regras específicas:

  • Uma promessa ou “thenable” é um objeto que fornece um padrão compatível com .then() método.uma promessa pendente pode passar para um estado cumprido ou rejeitado.,
  • uma promessa cumprida ou rejeitada é resolvida, e não deve transição para qualquer outro estado.
  • Uma vez que uma promessa é liquidada, ela deve ter um valor (que pode ser undefined). Esse valor não deve mudar.

a alteração neste contexto refere-se à comparação de identidade (===). Um objeto pode ser usado como o valor cumprido, e as propriedades do objeto podem sofrer mutações.,

Cada promessa tem de fornecer um .then() método com a seguinte assinatura:

promise.then(
onFulfilled?: Function,
onRejected?: Function
) => Promise

.then() método deve cumprir com estas normas:

  • O onFulfilled() e onRejected() são opcionais.
  • Se os argumentos fornecidos não são funções, eles devem ser ignorados.
  • onFulfilled() será chamado após a promessa ser cumprida, com o valor da promessa como o primeiro argumento.,
  • onRejected() será chamado após a promessa ser rejeitada, com a razão para a rejeição como o primeiro argumento. A razão pode ser qualquer valor JavaScript válido, mas como as rejeições são essencialmente sinônimo de exceções, eu recomendo o uso de objetos de erro.
  • Neither onFulfilled()noronRejected() may be called more than once.
  • .then() pode ser chamado muitas vezes na mesma promessa. Em outras palavras, uma promessa pode ser usada para agregar callbacks.,
  • .then() deve retornar uma nova promessa, promise2.
  • Se onFulfilled() ou onRejected() retornar um valor x e x é uma promessa, promise2 será bloqueado em com (suponha que o mesmo estado e valor) x. Caso contrário, promise2 será cumprido com o valor de x.,
  • Se onFulfilled ou onRejected lança uma exceção e promise2 deve ser rejeitada com e como o motivo.
  • Se onFulfilled não é uma função e promise1 é cumprida, promise2 deve ser preenchido com o mesmo valor promise1.,
  • Se onRejected não é uma função e promise1 é rejeitada, promise2 deve ser rejeitada, com a mesma razão promise1.

Promessa de Encadeamento

Porque .then() sempre retorna uma nova promessa, é possível que a cadeia de promessas com controle preciso sobre como e onde os erros são tratados. As promessas permitem-lhe imitar o comportamento normal do código síncrono

/catch.,

Como o código síncrono, acorrentamento resultará em uma sequência que corre em série., Em outras palavras, você pode fazer:

fetch(url)
.then(process)
.then(save)
.catch(handleErrors)
;

Aqui está um exemplo de um complexo promessa cadeia com várias rejeições:

Promessa de encadeamento de comportamento de exemplo no CodePen

Tratamento de Erro

Note que promete ter um sucesso e um manipulador de erro, e é muito comum ver o código que faz isso:

save().then(
handleSuccess,
handleError
);

Mas o que acontece se handleSuccess() lança um erro?, A promessa devolvida de .then() será rejeitada, mas não há nada lá para pegar a rejeição — o que significa que um erro em seu aplicativo é engolido. Oops!

Por essa razão, algumas pessoas consideram o código acima um anti-padrão, e recomendam o seguinte, em vez disso:

a diferença é sutil, mas importante. No primeiro exemplo, um erro originado na operação save() será capturado, mas um erro originado na função handleSuccess() será engolido.,

Without .catch(), an error in the success handler is uncaught.

In the second example, .catch() will handle rejections from either save(), or handleSuccess().

With .catch(), both error sources are handled., (diagrama de origem)

claro save() erro pode ser um erro de rede, considerando que o handleSuccess() erro pode ser porque o desenvolvedor esqueceu de lidar com um código de status específico. E se quiseres lidar com eles de forma diferente? Você pode optar por lidar com ambos:

save()
.then(
handleSuccess,
handleNetworkError
)
.catch(handleProgrammerError)
;

o que preferir, eu recomendo terminar todas as cadeias prometedoras com um .catch(). Vale a pena repetir:

eu recomendo terminar todas as cadeias prometedoras com um.catch().,

Como posso cancelar uma promessa?

uma das primeiras coisas que os novos usuários da promessa muitas vezes se perguntam é como cancelar uma promessa. Aqui está uma idéia: apenas rejeite a promessa com “cancelado” como a razão. Se você precisa lidar com isso de forma diferente de um erro “normal”, faça sua ramificação em seu manipulador de erros.

Aqui estão alguns erros comuns que as pessoas cometem quando rola o seu próprio cancelamento da promessa:

adicionando .,cancelar() a promessa

Adicionar .cancel() faz a promessa não-padrão, mas também viola uma outra regra de promessas: Apenas a função que cria a promessa deve ser capaz de resolver, rejeitar ou cancelar a promessa. Expô-lo quebra essa encapsulação, e encoraja as pessoas a escrever código que manipula a promessa em lugares que não deveriam saber sobre ela. Evite esparguete e promessas quebradas.

esquecendo-se de limpar

algumas pessoas inteligentes descobriram que há uma maneira de usar Promise.race() como um mecanismo de cancelamento., O problema com isso é que o controle de cancelamento é retirado da função que cria a promessa, que é o único lugar onde você pode realizar atividades de limpeza adequadas, tais como limpar timeouts ou liberar a memória, limpando referências a dados, etc…Esqueceu-se de lidar com uma promessa de cancelamento rejeitada

sabia que o Chrome lança mensagens de aviso por toda a consola quando se esquece de lidar com uma rejeição de promessa? Oops!

excessivamente complexo

a proposta de cancelamento retirada do TC39 propôs um canal de mensagens separado para cancelamentos., Ele também usou um novo conceito chamado um token de cancelamento. Na minha opinião, a solução teria inchado consideravelmente a especificação da promessa, e a única característica que teria fornecido que especulações não suportam diretamente é a separação de rejeições e cancelamentos, que, IMO, não é necessário para começar.

Você quer fazer a mudança dependendo se há uma exceção, ou um cancelamento? Sim, absolutamente. É esse o trabalho da promessa? Na minha opinião, não, não é.,

repensar o cancelamento da promessa

geralmente, passo toda a informação que a promessa precisa para determinar como resolver / rejeitar / cancelar no momento da criação da promessa. Dessa forma, não há necessidade de um método em uma promessa. Você pode estar se perguntando como você poderia saber se você vai ou não cancelar na hora de criação promessa.

“If I don’t yet know whether or not to cancel, how will I know what to pass in when I create the promise?,”

se ao menos houvesse algum tipo de objeto que pudesse estar em um valor potencial no futuro… Oh, espere.

O valor que passamos para representar se cancelar ou não poderia ser uma promessa em si. Veja como isso pode ser:

Anuláveis aguarde — tente no CodePen

Estamos utilizando o padrão de atribuição de parâmetro para dizer que não para cancelar por padrão. Isso torna o parâmetro cancel convenientemente opcional., Depois marcamos o tempo-limite como fizemos antes, mas desta vez capturamos a identificação do tempo-limite para que possamos limpá-la mais tarde.

usamos o método cancel.then() para lidar com o cancelamento e limpeza de recursos. Isto só funcionará se a promessa for cancelada antes de ter a oportunidade de resolver. Se cancelares demasiado tarde, perdes a tua oportunidade. Aquele comboio saiu da estação.

nota: pode estar a perguntar-se para que serve a funçãonoop(). A palavra noop significa no-op, significando uma função que não faz nada., Sem ele, V8 irá lançar avisos: UnhandledPromiseRejectionWarning: Unhandled promise rejection. É uma boa ideia lidar sempre com rejeições de promessas, mesmo que o seu manipulador seja um noop().

Abstraindo a Promessa de Cancelamento

Isso é bom para uma wait() timer, mas podemos resumo esta idéia para encapsular tudo o que você tem que lembrar:

  1. Rejeitar a cancelar sua promessa ao padrão — não queremos cancelar ou jogar erros se não cancelar a promessa de receber o passado.
  2. lembre-se de efectuar a limpeza quando rejeitar os cancelamentos.,
  3. lembre-se que o onCancel a limpeza pode por si só lançar um erro, e esse erro também vai precisar de ser manuseado. (Note que o tratamento de erros é omitido no exemplo de espera acima-é fácil de esquecer!)

vamos criar um utilitário de promessa cancelável que você pode usar para embrulhar qualquer promessa., Por exemplo, para lidar com solicitações de rede, etc… A assinatura será parecido com este:

speculation(fn: SpecFunction, shouldCancel: Promise) => Promise

O SpecFunction é apenas como a função que você gostaria de passar para o Promise construtor, com uma exceção — é preciso um onCancel() processador:

SpecFunction(resolve: Function, reject: Function, onCancel: Function) => Void

Note que este exemplo é apenas uma ilustração para dar-lhe a essência de como ele funciona. Há alguns outros casos extremos que você precisa levar em consideração., Por exemplo, nesta versão, handleCancel será chamado se você cancelar a promessa depois de já estar resolvida.

eu implementei uma versão de produção mantida deste com casos edge cobertos como a biblioteca open source, especulação.

Let’S use the improved library abstraction to rewrite the cancellable wait() utility from before., Primeiro instale a especulação:

npm install --save speculation

Agora você pode importar e utilizar:

Isso simplifica as coisas um pouco, porque você não tem de se preocupar com o noop(), a captura de erros no seu onCancel(), a função ou a outra extremidade dos casos. Esses detalhes foram extraídos por speculation(). Confira e sinta-se livre para usá-lo em projetos reais.,

Extras dos Nativos JS Promessa

O nativo Promise objeto tem algumas coisas que você poderia estar interessado em:

  • Promise.reject() retorna um rejeitada promessa.
  • Promise.resolve() devolve uma promessa resolvida.
  • Promise.race() toma um array (ou qualquer outro iterável) e devolve uma promessa que resolve com o valor da primeira promessa resolvida no iterável, ou rejeita com a razão da primeira promessa que rejeita.,
  • Promise.all() toma um array (ou qualquer outro iterável) e devolve uma promessa que resolve quando todas as promessas do argumento iterável foram resolvidas, ou rejeita com a razão da primeira promessa passada que rejeita.

Conclusion

Promises have become an integral part of several idioms in JavaScript, including the WHATWG Fetch standard used for most modern ajax requests, and the async Functions standard used to make asynchronous code look synchronous.,

Assíncrono funções de fase 3, no momento da redação deste texto, mas eu prevejo que em breve tornar-se muito popular, muito comumente usada solução para programação assíncrona em JavaScript, o que significa que aprende a apreciar as promessas vai ser ainda mais importante para JavaScript, os desenvolvedores no futuro próximo.

por exemplo, se você está usando Redux, eu sugiro que você confira redux-saga: uma biblioteca usada para gerenciar efeitos colaterais em Redux que depende das funções async ao longo da documentação.,espero que até os usuários experientes prometedores tenham uma melhor compreensão do que as promessas são e como elas funcionam, e como usá-las melhor depois de ler isso.

explorar a série

  • O que é um encerramento?qual é a diferença entre a herança de classe e a herança prototípica?o que é uma função pura?qual é a composição da função?o que é a programação funcional?o que é uma promessa?competências suaves

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *