解决异步问题,教你如何写出优雅的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 问题,希望对大家有帮助。



相关文章
|
20天前
|
前端开发 JavaScript 开发者
Async 和 Await 是基于 Promise 实现
【10月更文挑战第30天】Async和Await是基于Promise实现的语法糖,它们通过简洁的语法形式,借助Promise的异步处理机制,为JavaScript开发者提供了一种更优雅、更易于理解和维护的异步编程方式。
25 1
|
7天前
|
前端开发
如何使用async/await解决Promise的缺点?
总的来说,`async/await` 是对 Promise 的一种很好的补充和扩展,它为我们提供了更高效、更易读、更易维护的异步编程方式。通过合理地运用 `async/await`,我们可以更好地解决 Promise 的一些缺点,提升异步代码的质量和开发效率。
24 5
|
20天前
|
JSON 前端开发 JavaScript
浅谈JavaScript中的Promise、Async和Await
【10月更文挑战第30天】Promise、Async和Await是JavaScript中强大的异步编程工具,它们各自具有独特的优势和适用场景,开发者可以根据具体的项目需求和代码风格选择合适的方式来处理异步操作,从而编写出更加高效、可读和易于维护的JavaScript代码。
22 1
|
1月前
|
前端开发 JavaScript UED
深入了解JavaScript异步编程:回调、Promise与async/await
【10月更文挑战第11天】深入了解JavaScript异步编程:回调、Promise与async/await
19 0
|
6月前
|
前端开发 JavaScript
如何处理 JavaScript 中的异步操作和 Promise?
如何处理 JavaScript 中的异步操作和 Promise?
66 1
|
6月前
|
前端开发 JavaScript
在JavaScript中,什么是promise、怎么使用promise、怎么手写promise
在JavaScript中,什么是promise、怎么使用promise、怎么手写promise
99 4
|
6月前
|
前端开发 JavaScript 开发者
JavaScript 中的异步编程:Promise 和 Async/Await
在现代的 JavaScript 开发中,异步编程是至关重要的。本文将介绍 JavaScript 中的异步编程概念,重点讨论 Promise 和 Async/Await 这两种常见的处理异步操作的方法。通过本文的阐述,读者将能够更好地理解和应用这些技术,提高自己在 JavaScript 开发中处理异步任务的能力。
|
5月前
|
前端开发 JavaScript 开发者
JavaScript进阶-Promise与异步编程
【6月更文挑战第20天】JavaScript的Promise简化了异步操作,从ES6开始成为标准。Promise有三种状态:pending、fulfilled和rejected。基本用法涉及构造函数和`.then`处理结果,如: ```javascript new Promise((resolve, reject) => { setTimeout(resolve, 2000, '成功'); }).then(console.log); // 输出: 成功
91 4
|
6月前
|
JSON 前端开发 JavaScript
【JavaScript技术专栏】JavaScript异步编程:Promise、async/await解析
【4月更文挑战第30天】JavaScript中的异步编程通过Promise和async/await来解决回调地狱问题。Promise代表可能完成或拒绝的异步操作,有pending、fulfilled和rejected三种状态。它支持链式调用和Promise.all()、Promise.race()等方法。async/await是ES8引入的语法糖,允许异步代码以同步风格编写,提高可读性和可维护性。两者结合使用能更高效地处理非阻塞操作。
98 0
|
4月前
|
前端开发 JavaScript
JavaScript异步编程:Promise与async/await的深入探索
【7月更文挑战第9天】Promise和async/await是JavaScript中处理异步编程的两大利器。Promise为异步操作提供了统一的接口和链式调用的能力,而async/await则在此基础上进一步简化了异步代码的书写和阅读。掌握它们,将使我们能够更加高效地编写出清晰、健壮的异步JavaScript代码。

热门文章

最新文章

下一篇
无影云桌面