A JavaScript é a linguagem do frontend da Web, um ambiente com frequentes operações de IO e computações paralelas. A programação assíncrona é bastante comum em aplicações JavaScript. Apresento recursos de assincronismo da JS acompanhados de exemplos elementares de sua utilização.

Promessas

Promessas são objetos que quando resolvidos retornam um valor para a condição de sucesso ou outro para uma de falha. Dessa forma uma promessa é um objeto que possui um estado e um valor e que está associado a métodos que o resolvem ou o rejeitam.

// cria um objeto p do tipo promessa
const p = new Promise((resolve, reject) => {
    // resolve a promessa retornando um valor aleatório de par ou ímpar
    try {
        if ( Math.random() >= 0.5) {
            resolve("Par")
        }
        else {
            resolve("Ímpar")
        }
    //  rejeita a promessa em caso de erro
    } catch (e) {
        reject(e)
    }
}) 

Thenables

Um thenable é um objeto com o método then(onFullfiled[, onRejected]). Toda promessa é um thenable e como o método retorna uma promessa, ele é utilizado para encadear operações.

Outros métodos são o catch(onRejected) e o finally(onSettled). Onde catch(onCatch) é equivalente a then(onThen, onCatch) e finally(onFinally) é equivalente a then(onFinally, onFinally).

Nota: As funções ‘on…’ não são built-ins mas identificadores arbitrários que escolhi para representar os respectivos callbacks.

// Requisita a URL e passa o texto da página para uma função callback
let url = "https://www.google.com/robots.txt" 
fetch(url)
    .then(req => req.text())
    .then(callback)

Nota: Devido à política de CORS dos navegadores. Solicitações de recursos em endereços externos ao domínio da página são bloqueados. Em outras palavras, você precisa estar em site.com para solicitar um recurso em site.com.

O callback, um pedaço de código executável que é passado como argumento e executado futuramente, é a essência da programaçao assíncrona. No caso um callback é uma função que é passada como argumento para o método .then() e executada tendo o valor contido na promessa passado como argumento.

O callback é uma função qualquer que não seja um método. A exemplo:

// Armazena e imprime o texto da página
function printText(cb) {
    let text = cb
    console.log(text)
}

let url = "https://www.google.com/robots.txt" 
fetch(url)
    .then(req => req.text())
    // imprime o texto da página
    .then(printText)
    // imprime um eventual erro
    .catch(console.log) 

Async

O async é uma palavra-chave que faz com que a função seja executada sem bloqueio.

A invocação da função não cria uma promessa todavia o seu retorno, caso não seja uma promessa, é embrulhado como uma promessa resolvida. E caso não haja retorno, ela retorna uma promessa resolvida cujo valor é indefinido.

// retorna uma promessa resolvida cuja valor é 0
async function nihil() {
    return 0
}

Await

O await aguarda a resolução de uma promessa. Ele pausa a execução da função até que a promessa seja resolvida ou recusada e desembrulha uma promessa resolvida. Se o async é o yin, o await é o yang.

Ele não é sempre necessário mas é utilizado para evitar uma condição de corrida ou para desembrulhar uma promessa. A restrição é que ele apenas pode ser utilizado dentro de uma função async.

// Aguarda 1s e imprime um valor
async function countSec(n) {
     await new Promise(resolve => setTimeout(resolve, 1000))
     console.log(n)
}

// Conta de 1 a 5s
async function playHideSeek() {
    console.log("Vamos brincar de esconde-esconde...")
    for (let i=1; i<=5; i++) {
        // o await impede a condição de corrida
        await countSec(i)
    }
    console.log("Tô indo heim!")
}

playHideSeek()
// Requisita a URL e imprime dados JSON de uma API
let url = "https://jsonplaceholder.typicode.com/todos/1"
async function fetchData() {
    data = await fetch(url)
        .then(req => req.json())
        .catch(console.log)
    // sem o await acima, imprime-se a promessa
    console.log(data)
}

fetchData()

Ver também