Ordem de execução de async/await, promessa e setTimeout (mais recente em 2023)

Comece com um tópico

Vi uma pergunta na entrevista hoje sobre a ordem de execução de async/await, promessa e setTimeout . A pergunta é a seguinte:

async function async1() {
	console.log('async1 start');
	await async2();
	console.log('asnyc1 end');
}
async function async2() {
	console.log('async2');
}
console.log('script start');
setTimeout(() => {
	console.log('setTimeOut');
}, 0);
async1();
new Promise(function (reslove) {
	console.log('promise1');
	reslove();
}).then(function () {
	console.log('promise2');
})
console.log('script end');

 A resposta que dei:

script start
async1 start
async2
asnyc1 end // x
promise1
script end
promise2
setTimeOut

Resposta correta:

script start
async1 start
async2
promise1
script end
asnyc1 end
promise2
setTimeOut

Por que a promessa1 sai antes do final do asnyc1? Com esta pergunta, fui aprender sobre o mecanismo de loop de eventos.

mecanismo de loop de eventos js EventLoop

Existem dois tipos de eventos em JavaScript: 

 A sequência de execução de eventos é executar primeiro tarefas macro e depois executar micro tarefas. Esta é a base. As tarefas podem ter tarefas síncronas e tarefas assíncronas. As síncronas entram no thread principal e as assíncronas entram na tabela de eventos e se registram a função. Depois que o evento assíncrono for concluído, ele colocará a função de retorno de chamada na fila de eventos (tarefas macro e micro tarefas são filas de eventos diferentes). Depois que a tarefa de sincronização for executada, os eventos serão lidos na fila de eventos e colocados no thread principal para execução. A função de retorno de chamada também pode conter tarefas diferentes, portanto as operações acima são executadas em um loop.

Nota: setTimeOut não coloca diretamente sua função de retorno de chamada na fila assíncrona mencionada acima, mas coloca a função de retorno de chamada na fila assíncrona de execução após o término do tempo do cronômetro. Se já houver muitas tarefas nesta fila neste momento, coloque-as na fila atrás delas. Isso também explica porque setTimeOut não pode ser executado com precisão. A execução de setTimeOut precisa atender a duas condições:

O processo principal deve estar ocioso. Se o tempo acabar, o processo principal não executará sua função de retorno de chamada se não estiver ocioso.
Esta função de retorno de chamada não será executada até que todas as funções assíncronas anteriores tenham sido executadas quando ela for inserida no fila assíncrona.

promessa、assíncrono/aguardar

Em primeiro lugar, a nova Promise é uma tarefa síncrona e será colocada no processo principal para execução imediata. A função .then() significa que as tarefas assíncronas serão colocadas na fila assíncrona. Quando elas serão colocadas na fila assíncrona? Quando seu estado de promessa terminar, ele será imediatamente colocado na fila assíncrona.

Funções com a palavra-chave async retornarão um objeto de promessa . Se não houver await nele, a execução será equivalente a uma função comum; se não houver await, a função async não será muito poderosa, certo?

A palavra-chave await deve estar dentro da função de palavra-chave async. Se await for escrito fora, um erro será relatado; await, assim como sua semântica, está aguardando a conclusão da expressão à direita. Neste momento, await sairá do thread, bloqueará o código subsequente dentro do assíncrono e executará o código fora do assíncrono primeiro. O código subsequente interno não será executado até que o código de sincronização externo seja executado. Mesmo que await não seja um objeto de promessa, mas uma função sincronizada, ele ainda aguardará por esta operação.

Classificação de processos

Vamos revisar o processo de execução do código acima como um todo: 

  1. Todo o trecho de código (script) executa console.log('script start') como uma tarefa de macro e gera script start;
  2. A execução de setTimeout é uma ação assíncrona e é colocada na fila assíncrona da tarefa macro;
  3. Execute async1(), produza async1 start e continue a execução;
  4. Execute async2(), produza async2 e retorne um objeto de promessa. Await desiste do thread e adiciona a promessa retornada à fila assíncrona de microtarefas, portanto, o código abaixo de async1() também deve aguardar a conclusão do procedimento acima antes de continuar a execução;
  5. Execute a nova promessa, produza a promessa1 e, em seguida, coloque resolve() na fila assíncrona da microtarefa;
  6. Execute console.log('script end') e finalize o script de saída;
  7. Neste ponto, todos os códigos sincronizados foram executados e, em seguida, vá para a fila assíncrona de microtarefas para obter a tarefa.
  8. Em seguida, resolve (retornado pela promessa retornada por async2) é executado e o final async1 é gerado;
  9. Em seguida, execute resolve (new Promise) e produza promessa2;
  10. Finalmente, setTimeout é executado e settimeout é gerado. 

No passo 4, existe um mecanismo para await, que é a espera do await, que não bloqueará a execução de funções externas. Se await estiver aguardando uma Promise, o código na Promise ainda será executado de forma síncrona. Se não for uma Promise, o código na Promise ainda será executado de forma síncrona. Promessa, Promessa será usada. .resolve para encapsular, async2 aqui é um método assíncrono, a impressão interna será executada de forma síncrona e o código após await async2() será colocado na primeira posição na fila de microtarefas, aguardando o código de sincronização externa a ser executado.

Então eu sei por que o final do script gera prioridade sobre o final async1.

Acho que você gosta

Origin blog.csdn.net/YN2000609/article/details/132409910
Recomendado
Clasificación