1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
|
---
title: Usando promises
slug: Web/JavaScript/Guide/Using_promises
tags:
- Guía
- Intermediário
- JavaScript
- Promise
- Promises
translation_of: Web/JavaScript/Guide/Using_promises
original_slug: Web/JavaScript/Guide/Usando_promises
---
<div>{{jsSidebar("JavaScript Guide")}}{{PreviousNext("Web/JavaScript/Guide/Details_of_the_Object_Model", "Web/JavaScript/Guide/Iterators_and_Generators")}}</div>
<p class="summary">Uma {{jsxref("Promise")}} é um objeto que representa a eventual conclusão ou falha de uma operação assíncrona. Como a maioria das pessoas consomem promises já criadas, este guia explicará o consumo de promises devolvidas antes de explicar como criá-las.</p>
<p>Essencialmente, uma promise é um objeto retornado para o qual você adiciona callbacks, em vez de passar callbacks para uma função.</p>
<p>Por exemplo, em vez de uma função old-style que espera dois callbacks, e chama um deles em uma eventual conclusão ou falha:</p>
<pre class="brush: js line-numbers language-html language-js"><code class="language-js"><span class="keyword token">function</span> <span class="function token">successCallback</span><span class="punctuation token">(</span>result<span class="punctuation token">)</span> <span class="punctuation token">{</span>
console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span><span class="string token">"It succeeded with "</span> <span class="operator token">+</span> result<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="keyword token">function</span> <span class="function token">failureCallback</span><span class="punctuation token">(</span>error<span class="punctuation token">)</span> <span class="punctuation token">{</span>
console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span><span class="string token">"It failed with "</span> <span class="operator token">+</span> error<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="function token">doSomething</span><span class="punctuation token">(</span>successCallback<span class="punctuation token">,</span> failureCallback<span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
<p>…funções modernas retornam uma promise e então você pode adicionar seus callbacks:</p>
<pre class="brush: js line-numbers language-html language-js"><code class="language-js"><span class="keyword token">const</span> promise <span class="operator token">=</span> <span class="function token">doSomething</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
promise<span class="punctuation token">.</span><span class="function token">then</span><span class="punctuation token">(</span>successCallback<span class="punctuation token">,</span> failureCallback<span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
<p>…ou simplesmente:</p>
<pre class="brush: js line-numbers language-html language-js"><code class="language-js"><span class="function token">doSomething</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">.</span><span class="function token">then</span><span class="punctuation token">(</span>successCallback<span class="punctuation token">,</span> failureCallback<span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
<p>Nós chamamos isso de <em>chamada de função assíncrona</em>. Essa convenção tem várias vantagens. Vamos explorar cada uma delas.</p>
<h2 id="Garantias">Garantias</h2>
<p>Ao contrário dos callbacks com retornos de funções old-style, uma promise vem com algumas garantias:</p>
<ul>
<li>Callbacks nunca serão chamados antes da <a href="/en-US/docs/Web/JavaScript/EventLoop#Run-to-completion">conclusão da execução atual</a> do loop de eventos do JavaScript. </li>
<li>Callbacks adicionadas com .then mesmo <em>depois</em> do sucesso ou falha da operação assíncrona, serão chamadas, como acima.</li>
<li>Multiplos callbacks podem ser adicionados chamando-se .then várias vezes, para serem executados independentemente da ordem de inserção.</li>
</ul>
<p>Mas o benefício mais imediato das promises é o encadeamento.</p>
<h2 id="Encadeamento">Encadeamento</h2>
<p>Uma necessidade comum é executar duas ou mais operações assíncronas consecutivas, onde cada operação subsequente começa quando a operação anterior é bem sucedida, com o resultado do passo anterior. Nós conseguimos isso criando uma <em>cadeia de promises</em>.</p>
<p>Aqui está a mágica: a função <code>then</code> retorna uma nova promise, diferente da original:</p>
<pre class="brush: js">const promise = doSomething();
const promise2 = promise.then(successCallback, failureCallback);
</pre>
<p>ou</p>
<pre class="brush: js">const promise2 = doSomething().then(successCallback, failureCallback);
</pre>
<p>Essa segunda promise representa a conclusão não apenas de <code>doSomething()</code>, mas também do <code>successCallback</code> ou <code>failureCallback</code> que você passou, que podem ser outras funções assíncronas que retornam uma promise. Quando esse for o caso, quaisquer callbacks adicionados a <code>promise2</code> serão enfileiradas atrás da promise retornada por <code>successCallback</code> ou <code>failureCallback</code>.</p>
<p>Basicamente, cada promise representa a completude de outro passo assíncrono na cadeia.</p>
<p>Antigamente, realizar operações assíncronas comuns em uma linha levaria à clássica pirâmide da desgraça:</p>
<pre class="brush: js">doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
</pre>
<p>Ao invés disso, com funções modernas, nós atribuímos nossas callbacks às promises retornadas, formando uma <em>cadeia de promise</em>:</p>
<pre class="brush: js">doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
</pre>
<p>Os argumentos para <code>then</code> são opcionais, e <code>catch(failureCallback)</code> é uma abreviação para <code>then(null, failureCallback)</code>. Você pode também pode ver isso escrito com <a href="/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions">arrow functions</a>:</p>
<pre class="brush: js">doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
</pre>
<p><strong>Importante: </strong>Sempre retorne um resultado, de outra forma as callbacks não vão capturar o resultado da promise anterior.</p>
<h3 id="Encadeando_depois_de_um_catch">Encadeando depois de um catch</h3>
<p>É possivel encadear <em>depois </em>de uma falha, i.e um <code>catch</code>. Isso é muito útil para realizar novas ações mesmo depois de uma falha no encadeamento. Leia o seguinte exemplo: </p>
<pre class="brush: js">new Promise((resolve, reject) => {
console.log('Initial');
resolve();
})
.then(() => {
throw new Error('Something failed');
console.log('Do this');
})
.catch(() => {
console.log('Do that');
})
.then(() => {
console.log('Do this whatever happened before');
});
</pre>
<p>Isso vai produzir o seguinte texto:</p>
<pre>Initial
Do that
Do this whatever happened before
</pre>
<p>Observe que o texto "Do this" não foi impresso por conta que o erro "Something failed" causou uma rejeição.</p>
<h2 id="Propagação_de_erros">Propagação de erros</h2>
<p>Na pirâmide da desgraça vista anteriormente, você pode se lembrar de ter visto <code>failureCallback</code> três vezes, em comparação a uma única vez no fim da corrente de promises:</p>
<pre class="brush: js">doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => console.log(`Got the final result: ${finalResult}`))
.catch(failureCallback);
</pre>
<p>Basicamente, uma corrente de promises para se houver uma exceção, procurando por catch handlers no lugar. Essa modelagem de código segue bastante a maneira de como o código síncrono funciona:</p>
<pre class="brush: js">try {
const result = syncDoSomething();
const newResult = syncDoSomethingElse(result);
const finalResult = syncDoThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch(error) {
failureCallback(error);
}
</pre>
<p>Essa simetria com código assíncrono resulta no <em>syntactic sugar</em> <a href="/en-US/docs/Web/JavaScript/Reference/Statements/async_function"><code>async</code>/<code>await</code></a> presente no ECMAScript 2017:</p>
<pre class="brush: js">async function foo() {
try {
const result = await doSomething();
const newResult = await doSomethingElse(result);
const finalResult = await doThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch(error) {
failureCallback(error);
}
}
</pre>
<p>É construído sobre promises, por exemplo, <code>doSomething()</code> é a mesma função que antes. Leia mais sobre a sintaxe <a href="https://developers.google.com/web/fundamentals/getting-started/primers/async-functions">aqui</a>.</p>
<p>Por pegar todos os erros, até mesmo exceções jogadas(<em>thrown exceptions</em>) e erros de programação, as promises acabam por solucionar uma falha fundamental presente na pirâmide da desgraça dos callbacks. Essa característica é essencial para a composição funcional das operações assíncronas.</p>
<h2 id="Criando_uma_Promise_em_torno_de_uma_callback_API_antiga">Criando uma Promise em torno de uma callback API antiga</h2>
<p>Uma {{jsxref("Promise")}} pode ser criada do zero utilizando o seu construtor. Isto deve ser necessário apenas para o envolvimento de APIs antigas.</p>
<p>Em um mundo ideal, todas as funções assíncronas já retornariam promises. Infelizmente, algumas APIs ainda esperam que os retornos de sucesso e/ou falha sejam passados da maneira antiga. O exemplo por excelência é o {{domxref("WindowTimers.setTimeout", "setTimeout()")}} function:</p>
<pre class="brush: js">setTimeout(() => saySomething("10 seconds passed"), 10000);
</pre>
<p>Misturar chamadas de retorno e promises de <em>old-style</em> é problemático. Se <code>saySomething</code> falhar ou contiver um erro de programação, nada o captura.</p>
<p>Por sorte nós podemos envolvê-la em uma promise. É uma boa prática envolver funções problemáticas no menor nivel possível, e nunca chamá-las diretamente de novo:</p>
<pre class="brush: js">const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait(10000).then(() => saySomething("10 seconds")).catch(failureCallback);
</pre>
<p>Basicamente, um construtor de promises pega uma função executora que nos deixa resolver ou rejeitar uma promise manualmente. Desde que <code>setTimeout</code> não falhe, nós deixamos a rejeição de fora neste caso.</p>
<h2 id="Composição">Composição</h2>
<p>{{jsxref("Promise.resolve()")}} e {{jsxref("Promise.reject()")}} são atalhos para se criar manualmente uma promise que já foi resolvida ou rejeitada, respectivamente. Isso pode ser útil em algumas situações.</p>
<p>{{jsxref("Promise.all()")}} e {{jsxref("Promise.race()")}} são duas ferramentas de composição para se executar operações assíncronas em paralelo.</p>
<p>Uma composição sequencial é possível usando JavaScript de uma forma esperta:</p>
<pre class="brush: js">[func1, func2].reduce((p, f) => p.then(f), Promise.resolve());
</pre>
<p>Basicamente reduzimos um vetor de funções assíncronas a uma cadeia de promises equivalentes a: <code>Promise.resolve().then(func1).then(func2);</code></p>
<p>Isso também pode ser feito com uma função de composição reutilizável, que é comum em programação funcional:</p>
<pre class="brush: js">const applyAsync = (acc,val) => acc.then(val);
const composeAsync = (...funcs) => x => funcs.reduce(applyAsync, Promise.resolve(x));</pre>
<p>A função composeAsync aceitará qualquer número de funções como argumentos e retornará uma nova função que aceita um valor inicial a ser passado pelo pipeline de composição. Isso é benéfico porque alguma, ou todas as funções, podem ser assíncronas ou síncronas, e é garantido de que serão executadas na ordem correta.</p>
<pre class="brush: js">const transformData = composeAsync(func1, asyncFunc1, asyncFunc2, func2);
transformData(data);
</pre>
<p>No ECMAScript 2017, uma composição sequencial pode ser feita de forma mais simples com async/await:</p>
<pre class="brush: js">for (const f of [func1, func2]) {
await f();
}
</pre>
<h2 id="Cronometragem">Cronometragem</h2>
<p>Para evitar surpresas, funções passadas para <code>then</code> nunca serão chamadas sincronamente, mesmo com uma função já resolvida:</p>
<pre class="brush: js">Promise.resolve().then(() => console.log(2));
console.log(1); // 1, 2
</pre>
<p>Ao invés de rodar imediatamente, a função passada é colocada em uma micro tarefa, o que significa que ela roda depois que a fila estiver vazia no final do atual processo de evento de loop do Javascript, ou seja: muito em breve:</p>
<pre class="brush: js">const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait().then(() => console.log(4));
Promise.resolve().then(() => console.log(2)).then(() => console.log(3));
console.log(1); // 1, 2, 3, 4
</pre>
<h2 id="Ver_também">Ver também</h2>
<ul>
<li>{{jsxref("Promise.then()")}}</li>
<li><a href="http://promisesaplus.com/">Promises/A+ specification</a></li>
<li><a href="https://medium.com/@ramsunvtech/promises-of-promise-part-1-53f769245a53">Venkatraman.R - JS Promise (Part 1, Basics)</a></li>
<li><a href="https://medium.com/@ramsunvtech/js-promise-part-2-q-js-when-js-and-rsvp-js-af596232525c#.dzlqh6ski">Venkatraman.R - JS Promise (Part 2 - Using Q.js, When.js and RSVP.js)</a></li>
<li><a href="https://tech.io/playgrounds/11107/tools-for-promises-unittesting/introduction">Venkatraman.R - Tools for Promises Unit Testing</a></li>
<li><a href="http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html">Nolan Lawson: We have a problem with promises — Common mistakes with promises</a></li>
</ul>
<p>{{PreviousNext("Web/JavaScript/Guide/Details_of_the_Object_Model", "Web/JavaScript/Guide/Iterators_and_Generators")}}</p>
|