一次性让你懂async/await,解决回调地狱

终极思想

  • 如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
  • 如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面【阻塞的只是当前路径,并不阻塞其它路径的代码】的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。

示例一

代码

function testSometing() {
    console.log("testSomething");
    return "return testSomething";
}
async function testAsync() {
    console.log("testAsync");
    return Promise.resolve("hello async");
}
async function test() {
    console.log("test start...");
    const testFn1 = await testSometing();
    console.log(testFn1);
    const testFn2 = await testAsync();
    console.log(testFn2);
    console.log('test end...');
}
test();
var promiseFn = new Promise((resolve)=> { 
                    console.log("promise START...");
                    resolve("promise RESOLVE");
                });
promiseFn.then((val)=> console.log(val));
console.log("===END===")

运行结果

运行解说

(1)test()打印出”test start...“
(2)await testSomething(),根据”await后面的函数会先执行一遍,然后就会跳出整个async函数来执行后面js栈的代码“,会先执行testSometing()这个函数打印出“testSometing”的字符串,
(3)然后跳出async,执行promiseFn打印出“promise START...”,返回的Promiseresolve("promise RESOLVE")放入Promise队列
(4)继续执行主线程栈中的console.log("=END="),打印“=END=”
(5)根据”等本轮事件循环执行完了之后又会跳回到async函数中等待await后面表达式的返回值,如果返回值为非promise则继续执行async函数后面的代码,否则将返回的promise放入Promise队列“,跳回async函数,因为testSometing() 不是async函数,返回值为非promise,打印"return testSomething"
(6)test()函数继续执行,执行到testFn2(),执行testAsync(),打印"testAsync"
(7)然后跳出async,因为主线程没有任务,所以进入Promise队列,执行promiseFn.then((val)=> console.log(val));打印出“promise RESOLVE”
(8)跳回到test()继续执行console.log(testFn2)的返回值,打印出“hello async”
(9)最后打印“test end...”

示例二

加点料,让testSomething()变成async

代码

async function testSometing() {
    console.log("testSomething");
    return "return testSomething";
}
async function testAsync() {
    console.log("testAsync");
    return Promise.resolve("hello async");
}
async function test() {
    console.log("test start...");
    const testFn1 = await testSometing();
    console.log(testFn1);
    const testFn2 = await testAsync();
    console.log(testFn2);
    console.log('test end...');
}
test();
var promiseFn = new Promise((resolve)=> { 
                    console.log("promise START...");
                    resolve("promise RESOLVE");
                });
promiseFn.then((val)=> console.log(val));
console.log("===END===")

运行结果

运行解说

和上一个例子比较发现promiseFn.then((val)=> console.log(val)); 先于console.log(testFn1) 执行。
原因是因为现在的版本async函数会被await resolve,

补充

function testSometing() {
    console.log("testSomething");
    return "return testSomething";
}
console.log(Object.prototype.toString.call(testSometing)) // [object Function]
console.log(Object.prototype.toString.call(testSometing())) // [object String]

async function testSometing() {
    console.log("testSomething");
    return "return testSomething";
}
console.log(Object.prototype.toString.call(testSometing)) // [object AsyncFunction]
console.log(Object.prototype.toString.call(testSometing())) // [object Promise]

testSomething()已经是async函数,返回的是一个Promise对象需要等它 resolve 后将当前Promise 推入队列,随后先清空调用栈,所以会"跳出" test() 函数执行后续代码,随后才开始执行该 Promise。

宏任务 微任务举例

  在 JavaScript 中,宏任务和微任务的执行是由事件循环(Event Loop)机制来管理的。 事件循环是一个持续运行的循环,它负责监听任务队列并执行任务。当 JavaScript 引擎执行完当前的宏任务后,会检查是否存在微任务队列。如果存在微任务队列,引擎会依次执行所有的微任务,直到微任务队列为空。然后,引擎会继续执行下一个宏任务。
  下面以生活中的例子来说明宏任务和微任务的执行过程:
  假设你有一个待办事项列表,其中包含了一些宏任务和微任务。
  1. 宏任务:做饭、洗衣服、打扫房间。这些任务都需要一定的时间来完成,所以它们被视为宏任务。你可以按照自己的安排依次完成这些任务。
  2. 微任务:回复邮件、回复短信、支付账单。这些任务相对较小,可以很快完成,所以它们被视为微任务。你决定在完成每个宏任务之后,立即执行这些微任务。 在这个例子中,你按照以下顺序执行任务:
  - 开始做饭(宏任务)。
  - 在做饭过程中,你收到一封重要的邮件,决定先回复邮件(微任务)。
  - 继续做饭。
  - 做饭完成,开始洗衣服(宏任务)。
  - 在洗衣服过程中,你收到一条紧急的短信,决定先回复短信(微任务)。
  - 继续洗衣服。
  - 洗衣服完成,开始打扫房间(宏任务)。
  - 在打扫房间过程中,你想起还有一笔账单需要支付,决定先支付账单(微任务)。
  - 继续打扫房间。
  这个例子中,宏任务是做饭、洗衣服、打扫房间,微任务是回复邮件、回复短信、支付账单。你决定在每个宏任务之后立即执行微任务,以确保重要的事情能够及时处理。 通过这个生活例子,你可以更好地理解宏任务和微任务的执行过程。在 JavaScript 中,事件循环机制会帮助我们合理地安排任务的执行顺序,保证异步操作的顺序性和响应性。

文章转载部分内容地址

https://juejin.cn/post/6844903621360943118
https://segmentfault.com/a/1190000007535316

posted @ 2023-07-01 11:54  窦戈  阅读(253)  评论(0编辑  收藏  举报