解决异步问题,教你如何写出优雅的promise和async/await,告别callback回调地狱!(二)

简介: 解决异步问题——promise、async/await

三、async/await



现代 js 的异步开发,基本上被 asyncawait 给承包和普及了。虽然说 promise 中的 .then.catch 已经很简洁了,但是 async 更简洁,它可以通过写同步代码来执行异步的效果。如此神奇的 asyncawait 究竟是什么呢?让我们一起来一探究竟吧!


1、引例阐述


先用一个例子来展示 promiseasync/await 的区别。假设我们现在要用异步来实现加载图片。

(1) 如果用 promise.then.catch 实现时,代码如下:

function loadImg(src){
    const picture = new Promise(
        (resolve, reject) => {
            const img = document.createElement('img');
            img.onload = () => {
                resolve(img);
            }
            img.onerror = () => {
                const err = new Error(`图片加载失败 ${src}`);
                reject(err);
            }
            img.src = src;
        }
    )
    return picture;
}
const url1 = 'https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2144980717,2336175712&fm=58';
const url2 = 'https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=614367910,2483275034&fm=58';
loadImg(url1).then(img1 => {
    console.log(img1.width);
    return img1; //普通对象
}).then(img1 => {
    console.log(img1.height);
    return loadImg(url2); //promise 实例
}).then(img2 => {
    console.log(img2.width);
    return img2;
}).then(img2 => {
    console.log(img2.height);
}).catch(ex => console.error(ex));
复制代码

(2) 如果用 async 实现时,代码如下:

function loadImg(src){
    const picture = new Promise(
        (resolve, reject) => {
            const img = document.createElement('img');
            img.onload = () => {
                resolve(img);
            }
            img.onerror = () => {
                const err = new Error(`图片加载失败 ${src}`);
                reject(err);
            }
            img.src = src;
        }
    )
    return picture;
}
const url1 = 'https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2144980717,2336175712&fm=58';
const url2 = 'https://dss2.bdstatic.com/8_V1bjqh_Q23odCf/pacific/1990552278.jpg';
!(async function () {
    // img1
    const img1 = await loadImg(url1);
    console.log(img1.width, img1.height);
    // img2
    const img2 = await loadImg(url2);
    console.log(img2.width, img2.height);
})();

大家可以看到,如果用第二种方式的话,代码要优雅许多。且最关键的是,通过 asyncawait ,用同步代码就可以实现异步的功能。接下来我们开始来了解 asyncawait


2、async和await


  • 背景:解决异步回调问题,防止深陷回调地狱 Callback hell
  • PromisePromise then catch 是链式调用,但也是基于回调函数;
  • async/awaitasync/await同步语法,彻底消灭回调函数,是消灭异步回调的终极武器。


3、async/await和promise的关系


(1)  async/awaitpromise 并不互斥,两者相辅相成。

(2) 执行 async 函数,返回的是 promise 对象。

(3)  await 相当于 promisethen

(4)  try…catch 可以捕获异常,代替了 promisecatch

接下来我们来一一(2)(3)(4)演示这几条规则。

第一条规则: 执行 async 函数,返回的是 promise 对象。

async function fn1(){
    return 100; //相当于return promise.resolve(100);
}
const res1 = fn1(); //执行async函数,返回的是一个Promise对象
console.log('res1', res1); //promise对象
res1.then(data => {
    console.log('data', data); //100
});
复制代码

在以上的这段代码中,控制台打印结果如下。

69.png

大家可以看到,第一个 res1 返回的是一个 promise 对象,且此时 promise 对象的状态是 fulfilled 状态,所以可以调用后续的 .then 并且打印出 data 100

第二条规则:  await 相当于 promisethen

!(async function (){
    const p1 = Promise.resolve(300);
    const data = await p1; //await 相当于 promise的then
    console.log('data', data); //data 300
})();
!(async function () {
    const data1 = await 400; //await Promise.resolve(400)
    console.log('data1', data1); //data1 400
})();

在以上的这段代码中,控制台打印结果如下。

70.png

大家可以看到, p1 调用 resolve 回调函数,所以此时 p1 属于 fulfilled 状态,之后  const data = await p1 中的await,相当于 promisethen ,又因为此时 p1 属于 fulfilled 状态,所以可以对 .then 进行调用,于是输出 data 300 。同理在第二段代码中, await 400 时, 400 即表示 Promise.resolved(400) ,因此属于 fulfilled 状态,随后调用 .then ,打印出 data1 400 结果。

再来看一段代码:

!(async function (){
    const p2 = Promise.reject('err1');
    const res = await p4; //await 相当于 promise的then
    console.log('res', res); //不打印
})();
复制代码

在以上的这段代码中,控制台打印结果如下。

71.png

大家可以看到, p2 调用 reject 回调函数,所以此时 p2 属于 reject 状态。但因为await是触发 promise 中的 .then ,所以此时 res 不会被触发,于是后续不会对await进行操作,控制台也就不对 console.log('res', res); 进行打印。

第三条规则:  try…catch 可以捕获异常,代替了 promisecatch

!(async function () {
    const p3 = Promise.reject('err1'); //rejected 状态
    try{
        const res = await p3;
        console.log(res);
    }catch(ex){
        console.error(ex); //try…catch 相当于 promise的catch
    }
})();

在以上的这段代码中,控制台打印结果如下。

72.png

大家可以看到, p3 调用 reject 回调函数,所以此时 p3 属于 rejected 状态,因此它不会执行 try 的内容,而是去执行 catch 的内容, try…catch 中的 catch 就相当于 promise 中的 catch ,且此时 p3 属于 rejected 状态,因此执行 catch ,浏览器捕获到异常,报出错误。


4、异步的本质


从上面的分析中,不管是 promise 还是 async/await ,都是解决异步问题。但是呢,异步的本质还是解决同步的问题,所以,异步的本质是:

  • async/await 是消灭异步回调的终极武器;
  • JS 是单线程的,需要有异步,需要基于 event loop
  • async/await 是一个语法糖,但是这颗糖非常好用!!

我们来看两道 async/await 的顺序问题,回顾 async/await

第一题:

async function async1(){
    console.log('async start'); // 2
    await async2();
    //await 的后面,都可以看做是callback里面的内容,即异步。
    //类似event loop
    //Promise.resolve().then(() => console.log('async1 end'))
    console.log('async1 end'); // 5
}
async function async2(){
    console.log('async2'); //3
}
console.log('script start');  // 1
async1();
console.log('script end'); // 4

在以上的这段代码中,控制台打印结果如下。

73.png

从上面这段代码中可以看到,先执行同步代码 1 ,之后执行回调函数 async1() ,在回调函数 async1() 当中,先执行同步代码 2 ,之后遇到 await ,值得注意的是, await 的后面,都可以看作是 callback 里面的内容,即异步内容,所以,先执行 await 中对应的 async2() 里面的内容,之后把 await 后面所有的内容放置到异步当中。继续执行 4 ,等到 4 执行完时,整个同步代码已经执行完,最后,再去执行异步的代码,最终输出 5 的内容。

同样的方式来来分析第二题。

第二题:

async function async1(){
    console.log('async1 start'); //2
    await async2();
    // 下面三行都是异步回调,callback的内容
    console.log('async1 end'); //5
    await async3();
    // 下面一行是回调的内容,相当于异步回调里面再嵌套一个异步回调。
    console.log('async1 end 2'); //7
}
async function async2(){
    console.log('async2'); //3
}
async function async3(){
    console.log('async3'); //6
}
console.log('script start'); //1
async1();
console.log('script end'); //4

在以上的这段代码中,控制台打印结果如下。

74.png

这里就不再进行分析啦!大家可以根据第一个案例的步骤进行分析。


5、场景题


最后的最后,我们再来做两道题回顾我们刚刚讲过的 async/await 知识点。


(1)async/await语法


async function fn(){
  return 100;
}
(async function(){
    const a = fn(); //?? Promise
    const b = await fn(); //?? 100
})();
(async function(){
    console.log('start');
    const a = await 100;
    console.log('a', a);
    const b = await Promise.resolve(200);
    console.log('b', b);
    const c = await Promise.reject(300); //出错了,再往后都不会执行了
    console.log('c', c);
    console.log('end');
})(); //执行完毕,打印出哪些内容?
//start
//100
//200


(2)async/await的顺序问题

async function async1(){
    console.log('async1 start'); // 2
    await async2();
    //await后面的都作为回调内容 —— 微任务
    console.log('async1 end'); // 6
}
async function async2(){
    console.log('async2'); // 3
}
console.log('script start'); // 1
setTimeout(function(){ //宏任务 —— setTimeout
    console.log('setTimeout'); // 8
}, 0);
//遇到函数,立马去执行函数
async1();
//初始化promise时,传入的函数会立刻被执行
new Promise(function(resolve){ //promise —— 微任务
    console.log('promise1'); // 4
    resolve();
}).then(function(){ //微任务
    console.log('promise2'); // 7
});
console.log('script end'); // 5
//同步代码执行完毕(event loop —— call stack 被清空)
//执行微任务
//(尝试触发 DOM 渲染)
// 触发event loop,执行宏任务

这里就不再进行一一解析啦!大家可以前面知识点的学习总计再回顾理解。


四、写在最后



关于 js 的异步问题以及异步的解决方案问题就讲到这里啦!u1s1, promiseasync/await 在我们日常的前端开发中还是蛮重要的,基本上写异步代码时候都会用到 async/await 来解决。啃了16个小时总结了event looppromise 、async/await 问题,希望对大家有帮助。



相关文章
|
3天前
|
前端开发 JavaScript
Promise、async和await
Promise、async和await
11 0
|
1月前
|
前端开发 JavaScript 开发者
JavaScript 中的异步编程:Promise 和 Async/Await
在现代的 JavaScript 开发中,异步编程是至关重要的。本文将介绍 JavaScript 中的异步编程概念,重点讨论 Promise 和 Async/Await 这两种常见的处理异步操作的方法。通过本文的阐述,读者将能够更好地理解和应用这些技术,提高自己在 JavaScript 开发中处理异步任务的能力。
|
2天前
|
前端开发 JavaScript Java
Promise, async, await实现异步编程,代码详解
Promise, async, await实现异步编程,代码详解
13 1
|
23天前
|
前端开发
promise和async的区别是什么?
promise和async的区别是什么?
9 1
|
1月前
|
前端开发 JavaScript
js开发:请解释Promise是什么,以及它如何解决回调地狱(callback hell)问题。
Promise是JavaScript解决异步操作回调地狱的工具,代表未来可能完成的值。传统的回调函数嵌套导致代码难以维护,而Promise通过链式调用`.then()`和`.catch()`使异步流程清晰扁平。每个异步操作封装为Promise,成功时`.then()`传递结果,出错时`.catch()`捕获异常。ES6的`async/await`进一步简化Promise的使用,使异步代码更接近同步风格。
19 1
|
1月前
|
前端开发 JavaScript
如何处理 JavaScript 中的异步操作和 Promise?
如何处理 JavaScript 中的异步操作和 Promise?
15 1
|
1月前
|
前端开发 JavaScript
在JavaScript中,什么是promise、怎么使用promise、怎么手写promise
在JavaScript中,什么是promise、怎么使用promise、怎么手写promise
25 4
|
2月前
|
前端开发 JavaScript API
JavaScript学习笔记(一)promise与async
JavaScript学习笔记(一)promise与async
|
2月前
|
前端开发 JavaScript UED
JavaScript中的异步编程和Promise
【2月更文挑战第3天】在Web开发中,JavaScript是一门非常重要的编程语言,而异步编程是JavaScript中的一个关键概念。本文将介绍JavaScript中的异步编程特点,以及如何使用Promise来更加优雅地处理异步操作,帮助开发者更好地理解和应用这一技术。
17 3
|
2月前
|
前端开发 JavaScript 数据处理
JavaScript中的异步编程及Promise对象
【2月更文挑战第3天】 传统的JavaScript编程模式在处理异步任务时常常会导致回调地狱和代码可读性较差的问题,而Promise对象的引入为解决这一问题提供了一种优雅的解决方案。本文将介绍JavaScript中的异步编程方式以及Promise对象的使用方法和优势,帮助读者更好地理解和运用异步编程技术。
20 8