1 assíncrono, aguardar
2 Processos e threads do navegador
3 Macro tarefas e filas de microtarefas
4 Análise das perguntas da entrevista Promise
5 lançar, tentar, pegar, finalmente
Função assíncrona - Como escrever uma função assíncrona
// 普通函数 // function foo() {} // const bar = function() {} // const baz = () => {} // 生成器函数 // function* foo() {} // 异步函数 async function foo() { console.log("foo function1") console.log("foo function2") console.log("foo function3") } foo() // const bar = async function() {} // const baz = async () => {} // class Person { // async running() {} // }
Valor de retorno da função assíncrona - função assíncrona
// 返回值的区别 // 1.普通函数 // function foo1() { // return 123 // } // foo1() // 2.异步函数 async function foo2() { // 1.返回一个普通的值 // -> Promise.resolve(321) return ["abc", "cba", "nba"] // 2.返回一个Promise // return new Promise((resolve, reject) => { // setTimeout(() => { // resolve("aaa") // }, 3000) // }) // 3.返回一个thenable对象 // return { // then: function(resolve, reject) { // resolve("bbb") // } // } } foo2().then(res => { console.log("res:", res) })
Funções assíncronas – Exceções em funções assíncronas
Se houver erros ou exceções no código assíncrono, isso não afetará o código subsequente, mas os resultados serão passados na rejeição da promessa.
// "abc".filter() // 什么情况下异步函数的结果是rejected // 如果异步函数中有抛出异常(产生了错误), 这个异常不会被立即浏览器处理 // 进行如下处理: Promise.reject(error) async function foo() { console.log("---------1") console.log("---------2") // "abc".filter() throw new Error("coderwhy async function error") console.log("---------3") // return new Promise((resolve, reject) => { // reject("err rejected") // }) return 123 } // promise -> pending -> fulfilled/rejected foo().then(res => { console.log("res:", res) }).catch(err => { console.log("coderwhy err:", err) console.log("继续执行其他的逻辑代码") })
Uso de palavra-chave assíncrona de espera de função
Se await for usado simplesmente em variáveis e funções comuns, é inútil. É usado em conjunto com a promessa. Sua função é semelhante ao rendimento. É esperar até que a promessa retorne antes de receber o resultado e então executar as seguintes linhas de espera. (Isso interromperá a execução do código).
// 1.普通函数 // function foo1() { // await 123 // } // foo1() // 2.await关键字 // await条件: 必须在异步函数中使用 function bar() { console.log("bar function") return new Promise(resolve => { setTimeout(() => { resolve(123) }, 100000) }) } async function foo() { console.log("-------") // await后续返回一个Promise, 那么会等待Promise有结果之后, 才会继续执行后续的代码 const res1 = await bar() console.log("await后面的代码:", res1) const res2 = await bar() console.log("await后面的代码:", res2) console.log("+++++++") } foo()
Função assíncrona-await lida com solicitações assíncronas
Quando a promessa retorna rejeitada, o catch pode ser usado para capturar exceções e erros. Ou escreva try catch em getdata.
function requestData(url) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(url) // reject("error message") }, 2000); }) } async function getData() { const res1 = await requestData("why") console.log("res1:", res1) const res2 = await requestData(res1 + "kobe") console.log("res2:", res2) } getData().catch(err => { console.log("err:", err) })
Função assíncrona – combinação de await e async
Funções comuns retornam promessas, mas funções criadas com a palavra-chave assíncrona async também podem usar await ao chamar.
// 1.定义一些其他的异步函数 function requestData(url) { console.log("request data") return new Promise((resolve) => { setTimeout(() => { resolve(url) }, 3000) }) } async function test() { console.log("test function") return "test" } async function bar() { console.log("bar function") return new Promise((resolve) => { setTimeout(() => { resolve("bar") }, 2000); }) } async function demo() { console.log("demo function") return { then: function(resolve) { resolve("demo") } } } // 2.调用的入口async函数 async function foo() { console.log("foo function") const res1 = await requestData("why") console.log("res1:", res1) const res2 = await test() console.log("res2:", res2) const res3 = await bar() console.log("res3:", res3) const res4 = await demo() console.log("res4:", res4) } foo()
Thread único - como a sequência de código é executada
Cada página aberta pelo navegador se tornará um processo. Haverá muitos threads em um processo, mas haverá apenas um thread que executa JavaScript.
let name = "why" name = "kobe" function bar() { console.log("bar function") } function foo() { console.log("foo function") // 1.在JavaScript内部执行 // let total = 0 // for (let i = 0; i < 1000000; i++) { // total += i // } // 2.创建一个定时器 setTimeout(() => { console.log("setTimeout") }, 10000); bar() } foo()
Thread único - como o código assíncrono é executado
Independentemente do tempo definido pelo cronômetro, o código por trás do cronômetro será executado primeiro.
Fila de eventos, loop de eventos.
Timers, monitores DOM e solicitações de rede serão todos adicionados à fila de eventos e, em seguida, a fila de eventos insere eventos no contexto de execução em uma ordem de primeiro a entrar, primeiro a sair.
<body> <button>按钮</button> <script> const btn = document.querySelector("button") btn.onclick = function() { console.log("btn click event") } console.log("Hello World") let message = "aaaa" message = "bbbb" setTimeout(() => { console.log("10s后的setTimeout") }, 0); console.log("Hello JavaScript") console.log("代码继续执行~~~") console.log("-------------") </script> </body>
Single thread – a diferença entre microtarefas e macrotarefas
As funções dentro da promessa são iguais às funções escritas fora. Elas são executadas imediatamente e não são colocadas na fila de eventos. Mas se esta promessa executar resolver ou rejeitar, o código na função de retorno de chamada then e catch será colocado na fila.
O código do cronômetro será colocado na macrotarefa, e o código então será colocado na microtarefa.
A divisão entre tarefas macro e micro tarefas é estipulada e não escolhida por nós.
A função das microtarefas é executar antes das macrotarefas, portanto, as microtarefas são executadas antes das macrotarefas.
console.log("script start") // function bar() { // console.log("bar function") // } // function foo() { // console.log("foo function") // bar() // } // foo() // 定时器 setTimeout(() => { console.log("setTimeout0") }, 0) setTimeout(() => { console.log("setTimeout1") }, 0) // Promise中的then的回调也会被添加到队列中 console.log("1111111") new Promise((resolve, reject) => { console.log("2222222") console.log("-------1") console.log("-------2") resolve() console.log("-------3") }).then(res => { console.log("then传入的回调: res", res) }) console.log("3333333") console.log("script end")
Sequência de execução de código – pergunta 1 da entrevista (importante)
Observe que funções comuns e funções de retorno de chamada de promessa são executadas diretamente no contexto de execução, e o código então e a captura dos retornos de chamada de resolução e rejeição da promessa são executados em microtarefas.
Microtarefas são executadas antes das macrotarefas
A melhor maneira é desenhar a ordem de execução da fila de tarefas macro e da fila de micro tarefas
Se houver microtarefas na microtarefa, a macrotarefa será adiada até a execução, independentemente de a microtarefa ter sido concluída antecipadamente. Se você criar uma microtarefa ao executar uma macrotarefa (a macrotarefa já foi executada, só se pode dizer que a microtarefa terminou), após a conclusão da macrotarefa, você voltará imediatamente para executar a microtarefa .
Sequência final de execução:
console.log("script start") setTimeout(function () { console.log("setTimeout1"); new Promise(function (resolve) { resolve(); }).then(function () { new Promise(function (resolve) { resolve(); }).then(function () { console.log("then4"); }); console.log("then2"); }); }); new Promise(function (resolve) { console.log("promise1"); resolve(); }).then(function () { console.log("then1"); }); setTimeout(function () { console.log("setTimeout2"); }); console.log(2); queueMicrotask(() => { console.log("queueMicrotask1") }); new Promise(function (resolve) { resolve(); }).then(function () { console.log("then3"); }); console.log("script end")
Ordem de execução do código - aguardar código
Se then for executado em uma tarefa macro, a tarefa macro que a envolve será executada primeiro e depois a microtarefa.
O cronômetro também é dividido em tarefas macro avançadas de tempo curto.
Antes de await obter o resultado, o código subsequente neste escopo não será executado e será ignorado. E o código por trás do await no mesmo escopo é equivalente ao código do then, que é colocado em microtarefas.
console.log("script start") function requestData(url) { console.log("requestData") return new Promise((resolve) => { setTimeout(() => { console.log("setTimeout") resolve(url) }, 2000); }) } // 2.await/async async function getData() { console.log("getData start") const res = await requestData("why") console.log("then1-res:", res) console.log("getData end") } getData() console.log("script end") // script start // getData start // requestData // script end // setTimeout // then1-res: why // getData end
Pergunta 2 da entrevista sobre sequência de execução de código
Quando o async2() for executado na figura abaixo, a função asunc2 será chamada. Embora apenas async2 seja impresso, há um retorno oculto indefinido, que é equivalente a resolve(indefinido), e a linha console.log('async1 end') will É adicionado à microtarefa porque equivale a ser colocado em then .
Nota: O código de await async2() será executado como código normal como console.log('async1 start'), enquanto await async2() é uma função. Você precisa encontrar essa função e então pular para esta função para executar o código dentro.
async function async1 () { console.log('async1 start') await async2(); console.log('async1 end') } async function async2 () { console.log('async2') } console.log('script start') setTimeout(function () { console.log('setTimeout') }, 0) async1(); new Promise (function (resolve) { console.log('promise1') resolve(); }).then (function () { console.log('promise2') }) console.log('script end')
Tratamento de exceções - tratamento de erros padrão
Se o navegador relatar um erro, o código após a linha do código-fonte do erro não será executado. Então é perigoso.
Uma das soluções é lançar ativamente uma exceção, usando a palavra-chave throw. Mas o código por trás do throw também não será executado, a vantagem é que você pode lançar a descrição personalizada do seu problema.
// 1.遇到一个错误, 造成后续的代码全部不能执行 // function foo() { // "abc".filter() // console.log("第15行代码") // console.log("-------") // } // foo() // console.log("+++++++++") // const btn = document.querySelector("button") // btn.onclick = function() { // console.log("监听btn的点击") // } // 2.自己封装一些工具 function sum(num1, num2) { if (typeof num1 !== "number") { throw "type error: num1传入的类型有问题, 必须是number类型" } if (typeof num2 !== "number") { throw "type error: num2传入的类型有问题, 必须是number类型" } return num1 + num2 } // 李四调用 const result = sum(123, 321)
O lançamento de tratamento de exceção lança uma exceção
Se a informação da exceção lançada for uma frase, ela não terá efeito. Geralmente, o tipo de objeto é usado para lançá-la e o conteúdo que pode ser exibido é maior. Claro, você também pode personalizar a classe para escrever mensagens de erro.
O sistema também possui classes de erro escritas que podem ser usadas diretamente.
class HYError { constructor(message, code) { this.errMessage = message this.errCode = code } } // throw抛出一个异常 // 1.函数中的代码遇到throw之后, 后续的代码都不会执行 // 2.throw抛出一个具体的错误信息 function foo() { console.log("foo function1") // 1.number/string/boolean // throw "反正就是一个错误" // 2.抛出一个对象 // throw { errMessage: "我是错误信息", errCode: -1001 } // throw new HYError("错误信息", -1001) // 3.Error类: 错误函数的调用栈以及位置信息 throw new Error("我是错误信息") console.log("foo function2") console.log("foo function3") console.log("foo function4") } function bar() { foo() } bar()
Tratamento de exceções - como capturar exceções
Por que o código subsequente não é executado quando ocorre um erro?
Isso ocorre porque o erro é relatado camada por camada a partir do local onde o erro é relatado, e um erro eventualmente será lançado no navegador, de modo que a execução do código subsequente será interrompida. Se escrevermos outro código para capturar e manipular o erro, o código subsequente pode continuar a ser executado.
finalmente será definitivamente executado
function foo() { console.log("foo function1") // throw new Error("我是错误信息") console.log("foo function2") console.log("foo function3") console.log("foo function4") } function test() { // 自己捕获了异常的话, 那么异常就不会传递给浏览器, 那么后续的代码可以正常执行 try { foo() console.log("try后续的代码") } catch(error) { console.log("catch中的代码") // console.log(error) } finally { console.log("finally代码") } } function bar() { test() } bar() console.log("--------")
Depois de escrever o resultado de try catch para capturar a exceção, o código subsequente pode ser executado normalmente.