前端开发:Async/Await的使用详解

简介: 在ES7之前,了解到Promise是ES6为了解决异步回调而产生的解决方案,避免出现回调地狱(Callback Hell),那么ES7为什么又提出了新的Async/Await标准?问题答案就是:Promise虽然解决了异步嵌套的怪圈,使用表达清晰的链式表达;但是如果在实际开发过程中有些地方有大量的异步请求的时候,而且流程复杂嵌套的情况下,检查相关代码会发现一个比较尴尬。

前言

在ES7之前,了解到Promise是ES6为了解决异步回调而产生的解决方案,避免出现回调地狱(Callback Hell),那么ES7为什么又提出了新的Async/Await标准?问题答案就是:Promise虽然解决了异步嵌套的怪圈,使用表达清晰的链式表达;但是如果在实际开发过程中有些地方有大量的异步请求的时候,而且流程复杂嵌套的情况下,检查相关代码会发现一个比较尴尬的问题:无从下手。

2.png

一、Async/Await是什么?

Async/Await是基于Promise而来的,Async/Await是相互依存的,缺一不可,它们的出现就是为了Promise而来,也算是Promise的进化改良版,为的就是解决文章开始说的如果出现大量复杂嵌套不易读的Promise异步问题。

1、Async/Await基本含义

①Async/Await是基于Promise实现的,是写异步代码的新方式,它们不能用于普通的回调函数;

②Async/Await也是非阻塞的;

③Async/Await的写法使得异步代码看起来像同步代码,简洁、便于读取。

2、Async/Await的语法

async必须声明的是一个function函数,await就必须是在async声明的函数内部使用,这是一个固定搭配,任何一种不符合这两个条件,程序就会报错,具体举实例来直观看一下:

let data = 'data'
a = async function () {
    const b = function () {
        await data
    }
}

二、Async/Awaitd的本质

1、 Async的本质

async是对generator的再一次语法糖封装,帮助实现了生成器的调用,使语句更贴近同步代码的表达方式,可以将async函数看做是多个异步操作封装的 promise对象。

async声明的函数的返回其实就是一个Promise,也就是说只要声明的函数是async,不管内部怎么处理,它返回的一定是一个Promise,举个例子如下所示:

(async function () {
    return 'Promis+++‘
})() // 返回的是Promise+++

2、 Awaitd的本质

await的本质其实是可以提供等同于“同步效果”的等待异步返回能力的语法糖,也就是then的语法糖。如果想使用await来执行一个异步操作,那么其调用函数必须使用async来声明。

await能返回一个Promise对象,也能返回一个值。若await返回的是Promise对象,那么还可以继续给await的返回值使用then函数。举个实例看一下:

const a = async ()=>{
    let message = '声明值111’;
    let result = await message;
    console.log('由于上面的程序还没执行完,“等待一会”');
    return result;
}
a().then(result=>{
  console.log('输出',result);
})

三、 Async/Await的优势

为什么说Async/Awaitd比Promise更胜一筹?具体原因如下所示。

1、 简洁明了

根据上述关于Async/Awaitd的实例可以看到,Async/Awaitd的写法很简单,相比Promise的写法,不用写.then,不用写匿名函数处理Promise的resolve值,也不用定义多余的data变量,更避免了嵌套代码的操作,大大省去了很多代码行,使得处理异步操作的代码简洁明了,方便查阅和精准定位。

2、 错误处理的方式

Async/Await可以让try/catch同时处理同步和异步的错误,而且在Promise中try/catch不能处理JSON.parse的错误,在Promise中需要使用.catch,但是错误处理的那坨代码会非常冗余,要知道实际开发过程中代码会比理论上的情况会更复杂。

通过使用Async/Await,try/catch能处理JSON.parse的错误,具体实例如下所示:

    const request = async () => {
        try {
            const data = JSON.parse(await getJSON())
            console.log(data)
        } catch (err) {
            console.log(err)
        }
    }

3、 条件语句

通过使用Async/Await,可以使得条件语句写法简洁又可以提高代码可读性,这里就不再举对比的例子,只举一个Async/Await的使用实例来说:

const request = async () => {
  const data = await getJSON()
  if (data.anotherRequest) {
    const moreData = await anotherRequest(data);
    console.log(moreData)
    return moreData
  } else {
    console.log(data)
    return data    
  }
}

4、 中间值

在实际开发过程中会遇到这种场景:调用promise1,使用promise1返回的结果再去调用promise2,然后使用两者的结果去调用promise3。在没有使用Async/Await之前的写法,应该是这样的:

const request = () => {
     eturn promise1()
          .then(value1 => {
         return promise2(value1)
        .then(value2 => {        
               return promise3(value1, value2)
        })
    })
}

使用了Async/Await的写法之后,是这样的:

const request = async () => {
  const value1 = await promise1()
  const value2 = await promise2(value1)
  return promise3(value1, value2)
}

通过上述两个写法,直观的看出来使用Async/Await之后会使得代码变得非常整洁简单,直观,高可读性。

5、 错误栈对比

如果实例中调用多个Promise,其中的一个Promise出现错误,Promise链中返回的错误栈没有显示具体的错误发生的位置信息,这就造成排查错误的耗时时长和解决的难度,甚至会起到反作用,假设错误栈的唯一函数名为errorPromise,但是它和错误没有直接关系。如果使用了Async/Await之后,错误栈会直接指向错误所在的函数,这样更清晰直观的方便排查问题所在,尤其是在查看分析错误日志的时候非常有效有用。

6、 调试

通过上面描述的Async/Await优点中,一直在反复强调Async/Await会使得代码简洁明了,其实在调试过程中,Async/Await也可以使得代码调试起来很轻松简单,相对于Promise来讲,不用再写太多箭头函数,可以直接像调试同步代码一样单步走,跳过await语句。

7、 中断/终止程序

首先要明确知道,Promise自身是不能终止的,Promise本身只是一个状态机,存储了三种状态,一旦进行发出请求,就必须要闭环,无法进行取消操作,就算是在前面讲到的pending状态,也只是一个挂起请求的状态,但不是取消。

但是使用Async/Await的时候,想要终止程序就很简单,那是因为Async/Await语义化很明显,和一般的function的写法类似,想要终端程序的时候,直接return一个值(“”、null、false等)就可以了,实际上就是直接返回一个Promise。具体实例如下所示:

let count = 3;
const a = async ()=>{
  const result = await delay(2000);
  const result1 = await delaySecond(count);
  if (count > 2) {
      return '';
    // return false; 
    // return null;
  }
  console.log(await delay(2000));
  console.log(‘结束’);
};
a().then(result=>{
  console.log(result);
})
.catch(err=>{
  console.log(err);
})

async函数本质就是返回一个Promise

四、实际开发过程中异步操作需要注意的事项

Async/Await使用for循环获取数据(串行)

根据上面Promise的for循环获取数据来做对比,直接使用上述实例的场景,来看看Async/Await的写法,具体操作如下所示:

(async ()=>{
    array = [timeout(2000), timeout(1000), timeout(1000)]
    for (var i=0; i < array.length; i++) {
        result = await array[i]();
        console.log(result);
    }
})()

最后

通过对比Promise,在这里还要夸一下Async/Await,直观的可以看到同样的需求,使用Async/Await来实现是不是非常的方便和简洁。而且在前端面试求职的时候该知识点也是必问知识点,其重要性不言而喻,这里就不再赘述。以上就是本章全部内容,欢迎关注!

相关文章
|
5月前
|
前端开发 JavaScript 开发者
前端开发中的异步编程:Promise 和 Async/Await 的比较与应用
在现代前端开发中,异步编程是不可或缺的技术。本文将深入探讨Promise和Async/Await这两种主流的异步编程方式,分析它们的优劣势及在实际项目中的应用场景。通过比较它们的语法、可读性和错误处理机制,帮助开发者更好地选择和理解如何在项目中高效地利用这些技术。
|
6月前
|
前端开发 JavaScript 开发者
No102.精选前端面试题,享受每天的挑战和学习(async/await)
No102.精选前端面试题,享受每天的挑战和学习(async/await)
|
3月前
|
前端开发 JavaScript 开发者
探索前端开发中的异步编程:Promise与Async/Await
在现代前端开发中,处理异步操作是至关重要的。本文将深入探讨异步编程的核心概念,重点比较JavaScript中的Promise与Async/Await两种异步编程方式。通过实例和比较,读者将能够理解这两种方法的优缺点,如何在实际开发中选择适合的异步编程模式,从而编写更简洁、可维护的代码。
|
6月前
|
前端开发 JavaScript UED
前端 js 经典:async 和 await
前端 js 经典:async 和 await
73 2
|
6月前
|
Dart 前端开发 API
【Flutter前端技术开发专栏】Flutter中的异步编程与Future/async/await
【4月更文挑战第30天】本文探讨了Flutter中的异步编程,强调其在提高应用响应性和性能上的重要性。Flutter使用`Future`对象表示异步操作的结果,通过`.then()`和`.catchError()`处理异步任务。此外,Dart的`async/await`关键字简化了异步代码,使其更易读。理解并运用这些概念对于开发高效的Flutter应用至关重要。
47 0
【Flutter前端技术开发专栏】Flutter中的异步编程与Future/async/await
|
6月前
|
前端开发 JavaScript
前端JavaScript中异步的终极解决方案:async/await
在深入讨论 async/await 之前,我们需要了解一下 JavaScript 的单线程和非阻塞的特性。JavaScript 是单线程的,也就是说在任何给定的时间点,只能执行一个操作。然而,对于需要大量时间的操作(例如从服务器获取数据),如果没有适当的管理机制,这种单线程特性可能会导致应用程序的阻塞。为了解决这个问题,JavaScript 引入了回调函数和后来的 Promise,用来管理这些异步操作。
|
6月前
|
前端开发 JavaScript 开发者
【面试题】前端人70%以上 不了解的promise/async await
【面试题】前端人70%以上 不了解的promise/async await
135 0
|
1月前
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
127 2
|
1月前
|
JavaScript 前端开发 程序员
前端学习笔记——node.js
前端学习笔记——node.js
37 0
|
1月前
|
人工智能 自然语言处理 运维
前端大模型应用笔记(一):两个指令反过来说大模型就理解不了啦?或许该让第三者插足啦 -通过引入中间LLM预处理用户输入以提高多任务处理能力
本文探讨了在多任务处理场景下,自然语言指令解析的困境及解决方案。通过增加一个LLM解析层,将复杂的指令拆解为多个明确的步骤,明确操作类型与对象识别,处理任务依赖关系,并将自然语言转化为具体的工具命令,从而提高指令解析的准确性和执行效率。