Resumo : neste tutorial, você aprenderá sobre as promessas do JavaScript e como usá-las de maneira eficaz.
Por que o JavaScript promete
O exemplo a seguir define uma função getUsers()
que retorna uma lista de objetos de usuário :
function getUsers() {
return [
{ username: 'john', email: '[email protected]' },
{ username: 'jane', email: '[email protected]' },
];
}
Linguagem de código: JavaScript ( javascript )
Cada objeto de usuário possui duas propriedades username
e email
.
Para encontrar um usuário pelo nome de usuário na lista de usuários retornada pela getUsers()
função, você pode usar a findUser()
função da seguinte maneira:
function findUser(username) {
const users = getUsers();
const user = users.find((user) => user.username === username);
return user;
}
Linguagem de código: JavaScript ( javascript )
Na findUser()
função:
- Primeiro, obtenha um array de usuário chamando a
getUsers()
função - Segundo, encontre o usuário específico
username
usando ofind()
método doArray
objeto. - Terceiro, retorne o usuário correspondente.
O seguinte mostra o código completo para encontrar um usuário com o nome de usuário 'john'
:
function getUsers() {
return [
{ username: 'john', email: '[email protected]' },
{ username: 'jane', email: '[email protected]' },
];
}
function findUser(username) {
const users = getUsers();
const user = users.find((user) => user.username === username);
return user;
}
console.log(findUser('john'));
Linguagem de código: JavaScript ( javascript )
Saída:
{ username: 'john', email: '[email protected]' }
Linguagem de código: CSS ( css )
O código na findUser()
função é síncrono e bloqueador. A findUser()
função executa a getUsers()
função para obter um array de usuário, chama o find()
método no users
array para procurar um usuário com um nome de usuário específico e retorna o usuário correspondente.
Na prática, a getUsers()
função pode acessar um banco de dados ou chamar uma API para obter a lista de usuários. Portanto, a getUsers()
função terá um atraso.
Para simular o atraso, você pode usar a setTimeout()
função. Por exemplo:
function getUsers() {
let users = [];
// delay 1 second (1000ms)
setTimeout(() => {
users = [
{ username: 'john', email: '[email protected]' },
{ username: 'jane', email: '[email protected]' },
];
}, 1000);
return users;
}
Linguagem de código: JavaScript ( javascript )
Como funciona.
- Primeiro, defina um array
users
e inicialize seu valor com um array vazio. - Segundo, atribua um array de usuários à
users
variável dentro do retorno de chamada dasetTimeout()
função. - Terceiro, retorne o
users
array
O getUsers()
não funcionará corretamente e sempre retornará um array vazio. Portanto, a findUser()
função não funcionará conforme o esperado:
function getUsers() {
let users = [];
setTimeout(() => {
users = [
{ username: 'john', email: '[email protected]' },
{ username: 'jane', email: '[email protected]' },
];
}, 1000);
return users;
}
function findUser(username) {
const users = getUsers(); // A
const user = users.find((user) => user.username === username); // B
return user;
}
console.log(findUser('john'));
Linguagem de código: JavaScript ( javascript )
Saída:
undefined
Linguagem de código: JavaScript ( javascript )
Como getUsers()
retorna um array vazio, o users
array está vazio (linha A). Ao chamar o find()
método no users
array, o método retorna undefined
(linha B)
O desafio é como acessar o users
retornado da getUsers()
função após um segundo. Uma abordagem clássica é usar o callback .
Usando retornos de chamada para lidar com uma operação assíncrona
O exemplo a seguir adiciona um argumento de retorno de chamada às funções getUsers()
e :findUser()
function getUsers(callback) {
setTimeout(() => {
callback([
{ username: 'john', email: '[email protected]' },
{ username: 'jane', email: '[email protected]' },
]);
}, 1000);
}
function findUser(username, callback) {
getUsers((users) => {
const user = users.find((user) => user.username === username);
callback(user);
});
}
findUser('john', console.log);
Linguagem de código: JavaScript ( javascript )
Saída:
{ username: 'john', email: '[email protected]' }
Linguagem de código: CSS ( css )
Neste exemplo, a getUsers()
função aceita uma função de retorno de chamada como argumento e a invoca com o users
array dentro da setTimeout()
função. Além disso, a findUser()
função aceita uma função de retorno de chamada que processa o usuário correspondente.
A abordagem de retorno de chamada funciona muito bem. No entanto, isso torna o código mais difícil de seguir. Além disso, adiciona complexidade às funções com argumentos de retorno de chamada.
Se o número de funções aumentar, você poderá acabar com o problema do inferno de retorno de chamada. Para resolver isso, o JavaScript surge com o conceito de promessas.
Compreendendo as promessas do JavaScript
Por definição, uma promessa é um objeto que encapsula o resultado de uma operação assíncrona .
Um objeto de promessa possui um estado que pode ser um dos seguintes:
- Pendente
- Cumprido com um valor
- Rejeitado por um motivo
No início, o estado de uma promessa está pendente, indicando que a operação assíncrona está em andamento. Dependendo do resultado da operação assíncrona, o estado muda para cumprido ou rejeitado.
O estado cumprido indica que a operação assíncrona foi concluída com êxito:
O estado rejeitado indica que a operação assíncrona falhou.
Criando uma promessa
Para criar um objeto de promessa, você usa o Promise()
construtor:
const promise = new Promise((resolve, reject) => {
// contain an operation
// ...
// return the state
if (success) {
resolve(value);
} else {
reject(error);
}
});
Linguagem de código: JavaScript ( javascript )
O construtor da promessa aceita uma função de retorno de chamada que normalmente executa uma operação assíncrona. Essa função costuma ser chamada de executor.
Por sua vez, o executor aceita duas funções de retorno de chamada com o nome resolve
e reject
.
Observe que as funções de retorno de chamada passadas para o executor são resolve
e reject
apenas por convenção.
Se a operação assíncrona for concluída com êxito, o executor chamará a resolve()
função para alterar o estado da promessa de pendente para cumprida com um valor.
Em caso de erro, o executor chamará a reject()
função para alterar o estado da promessa de pendente para rejeitada com o motivo do erro.
Quando uma promessa atinge um estado cumprido ou rejeitado, ela permanece nesse estado e não pode ir para outro estado.
Em outras palavras, uma promessa não pode ir de fulfilled
estado para rejected
estado e vice-versa. Além disso, não pode voltar de um fulfilled
estado rejected
para outro pending
.
Depois que um novo Promise
objeto é criado, seu estado fica pendente. Se uma promessa for alcançada fulfilled
ou rejected
declarada, ela será resolvida .
Observe que você raramente criará objetos de promessa na prática. Em vez disso, você consumirá promessas fornecidas pelas bibliotecas.
Consumindo uma promessa: então, pegue, finalmente
1) O método then()
Para obter o valor de uma promessa quando ela for cumprida, você chama o then()
método do objeto da promessa. O seguinte mostra a sintaxe do then()
método:
promise.then(onFulfilled,onRejected);
Linguagem de código: CSS ( css )
O then()
método aceita duas funções de retorno de chamada: onFulfilled
e onRejected
.
O then()
método chama the onFulfilled()
com um valor, se a promessa for cumprida ou onRejected()
com um erro se a promessa for rejeitada.
Observe que ambos os argumentos onFulfilled
e onRejected
são opcionais.
O exemplo a seguir mostra como usar then()
o método do Promise
objeto retornado pela getUsers()
função:
function getUsers() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([
{ username: 'john', email: '[email protected]' },
{ username: 'jane', email: '[email protected]' },
]);
}, 1000);
});
}
function onFulfilled(users) {
console.log(users);
}
const promise = getUsers();
promise.then(onFulfilled);
Linguagem de código: JavaScript ( javascript )
Saída:
[
{ username: 'john', email: '[email protected]' },
{ username: 'jane', email: '[email protected]' }
]
Linguagem de código: JavaScript ( javascript )
Neste exemplo:
- Primeiro, defina a
onFulfilled()
função a ser chamada quando a promessa for cumprida. - Segundo, chame a
getUsers()
função para obter um objeto de promessa. - Terceiro, chame o
then()
método do objeto de promessa e envie a lista de usuários para o console.
Para tornar o código mais conciso, você pode usar uma função de seta como argumento do then()
método como este:
function getUsers() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([
{ username: 'john', email: '[email protected]' },
{ username: 'jane', email: '[email protected]' },
]);
}, 1000);
});
}
const promise = getUsers();
promise.then((users) => {
console.log(users);
});
Linguagem de código: JavaScript ( javascript )
Como a getUsers()
função retorna um objeto de promessa, você pode encadear a chamada da função com o then()
método assim:
// getUsers() function
//...
getUsers().then((users) => {
console.log(users);
});
Linguagem de código: JavaScript ( javascript )
Neste exemplo, a getUsers()
função sempre é bem-sucedida. Para simular o erro, podemos usar um success
sinalizador como o seguinte:
let success = true;
function getUsers() {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (success) {
resolve([
{ username: 'john', email: '[email protected]' },
{ username: 'jane', email: '[email protected]' },
]);
} else {
reject('Failed to the user list');
}
}, 1000);
});
}
function onFulfilled(users) {
console.log(users);
}
function onRejected(error) {
console.log(error);
}
const promise = getUsers();
promise.then(onFulfilled, onRejected);
Linguagem de código: JavaScript ( javascript )
Como funciona.
Primeiro, defina a success
variável e inicialize seu valor como true
.
Se o sucesso for true
, a promessa na getUsers()
função é cumprida com uma lista de usuários. Caso contrário, será rejeitado com uma mensagem de erro.
Em segundo lugar, defina as funções onFulfilled
e onRejected
.
Terceiro, obtenha a promessa da getUsers()
função e chame o then()
método com as funções onFulfilled
e onRejected
.
O seguinte mostra como usar as funções de seta como argumentos do then()
método:
// getUsers() function
// ...
const promise = getUsers();
promise.then(
(users) => console.log,
(error) => console.log
);
Linguagem de código: JavaScript ( javascript )
2) O método catch()
Se você deseja obter o erro somente quando o estado da promessa for rejeitado, você pode usar o catch()
método do Promise
objeto:
promise.catch(onRejected);
Linguagem de código: CSS ( css )
Internamente, o catch()
método invoca o then(undefined, onRejected)
método.
O exemplo a seguir altera o success
sinalizador para false
para simular o cenário de erro:
let success = false;
function getUsers() {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (success) {
resolve([
{ username: 'john', email: '[email protected]' },
{ username: 'jane', email: '[email protected]' },
]);
} else {
reject('Failed to the user list');
}
}, 1000);
});
}
const promise = getUsers();
promise.catch((error) => {
console.log(error);
});
Linguagem de código: JavaScript ( javascript )
3) O método finalmente()
Às vezes, você deseja executar o mesmo trecho de código, independentemente de a promessa ser cumprida ou rejeitada. Por exemplo:
const render = () => {
//...
};
getUsers()
.then((users) => {
console.log(users);
render();
})
.catch((error) => {
console.log(error);
render();
});
Linguagem de código: JavaScript ( javascript )
Como você pode ver, a render()
chamada de função é duplicada nos métodos then()
e catch()
.
Para remover esta duplicata e executar render()
se a promessa foi cumprida ou rejeitada, você usa o finally()
método assim:
const render = () => {
//...
};
getUsers()
.then((users) => {
console.log(users);
})
.catch((error) => {
console.log(error);
})
.finally(() => {
render();
});
Linguagem de código: JavaScript ( javascript )
Um exemplo prático de promessa de JavaScript
O exemplo a seguir mostra como carregar um arquivo JSON do servidor e exibir seu conteúdo em uma página da web.
Suponha que você tenha o seguinte arquivo JSON:
https://tutorials.acervolima.com/sample/promise/api.json
Linguagem de código: JavaScript ( javascript )
com o seguinte conteúdo:
{
"message": "JavaScript Promise Demo"
}
Linguagem de código: JSON/JSON com comentários ( json )
O seguinte mostra a página HTML que contém um botão. Ao clicar no botão, a página carrega os dados do arquivo JSON e mostra a mensagem:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JavaScript Promise Demo</title>
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<div id="container">
<div id="message"></div>
<button id="btnGet">Get Message</button>
</div>
<script src="js/promise-demo.js">
</script>
</body>
</html>
Linguagem de código: HTML, XML ( xml )
O seguinte mostra o arquivo promessa-demo.js:
function load(url) {
return new Promise(function (resolve, reject) {
const request = new XMLHttpRequest();
request.onreadystatechange = function () {
if (this.readyState === 4 && this.status == 200) {
resolve(this.response);
} else {
reject(this.status);
}
};
request.open('GET', url, true);
request.send();
});
}
const url = 'https://tutorials.acervolima.com/sample/promise/api.json';
const btn = document.querySelector('#btnGet');
const msg = document.querySelector('#message');
btn.addEventListener('click', () => {
load(URL)
.then((response) => {
const result = JSON.parse(response);
msg.innerHTML = result.message;
})
.catch((error) => {
msg.innerHTML = `Error getting the message, HTTP status: ${error}`;
});
});
Linguagem de código: JavaScript ( javascript )
Como funciona.
Primeiro, defina a load()
função que utiliza o XMLHttpRequest
objeto para carregar o arquivo JSON do servidor:
function load(url) {
return new Promise(function (resolve, reject) {
const request = new XMLHttpRequest();
request.onreadystatechange = function () {
if (this.readyState === 4 && this.status == 200) {
resolve(this.response);
} else {
reject(this.status);
}
};
request.open('GET', url, true);
request.send();
});
}
Linguagem de código: JavaScript ( javascript )
No executor, chamamos resolve()
a função com a Resposta se o código de status HTTP for 200. Caso contrário, invocamos a reject()
função com o código de status HTTP.
Em segundo lugar, registre o ouvinte de evento de clique do botão e chame o then()
método do objeto de promessa. Se o carregamento for bem-sucedido, mostramos a mensagem retornada do servidor. Caso contrário, mostramos a mensagem de erro com o código de status HTTP.
const url = 'https://tutorials.acervolima.com/sample/promise/api.json';
const btn = document.querySelector('#btnGet');
const msg = document.querySelector('#message');
btn.addEventListener('click', () => {
load(URL)
.then((response) => {
const result = JSON.parse(response);
msg.innerHTML = result.message;
})
.catch((error) => {
msg.innerHTML = `Error getting the message, HTTP status: ${error}`;
});
});
Linguagem de código: JavaScript ( javascript )
Resumo
- Uma promessa é um objeto que encapsula o resultado de uma operação assíncrona.
- Uma promessa começa no estado pendente e termina no estado cumprido ou rejeitado.
- Use
then()
o método para agendar um retorno de chamada a ser executado quando a promessa for cumprida ecatch()
o método para agendar um retorno de chamada a ser invocado quando a promessa for rejeitada. - Coloque o código que você deseja executar no
finally()
método, independentemente de a promessa ser cumprida ou rejeitada.