“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:
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()
eonRejected()
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()
ouonRejected()
retornar um valorx
ex
é uma promessa,promise2
será bloqueado em com (suponha que o mesmo estado e valor)x
. Caso contrário,promise2
será cumprido com o valor dex
., - Se
onFulfilled
ouonRejected
lança uma exceçãoe
promise2
deve ser rejeitada come
como o motivo. - Se
onFulfilled
não é uma função epromise1
é cumprida,promise2
deve ser preenchido com o mesmo valorpromise1
., - Se
onRejected
não é uma função epromise1
é rejeitada,promise2
deve ser rejeitada, com a mesma razãopromise1
.
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:
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.,
In the second example, .catch()
will handle rejections from either save()
, or handleSuccess()
.
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 rejeitadasabia 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:
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ção
noop()
. 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 umnoop()
.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:
- 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.
- lembre-se de efectuar a limpeza quando rejeitar os cancelamentos.,
- 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) => PromiseO SpecFunction é apenas como a função que você gostaria de passar para o
Promise
construtor, com uma exceção — é preciso umonCancel()
processador:SpecFunction(resolve: Function, reject: Function, onCancel: Function) => VoidNote 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 speculationAgora 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 seuonCancel()
, a função ou a outra extremidade dos casos. Esses detalhes foram extraídos porspeculation()
. 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