"深入探究 Async/Await:现代 JavaScript 异步编程的全貌"

前言

async await是一种用于编写异步代码的编程技术,它简化了异步操作的处理,使代码看起来和表现得更像同步代码。

回调函数

在讲解async await之前我们先来了解一下什么是回调函数!

回调函数是一种常见的编程模式,特别是在异步编程中。它是一种函数,作为参数传递给另一个函数,并且在某个特定事件发生或条件满足时被调用。

我们来看一段代码:

js 复制代码
function a(){
  setTimeout(() => {
    console.log("A");
  },1000)
}
function b(){
  setTimeout(()=>{
    console.log("B")
  })
}

我们可以看出结果为BA,接下来我们可以思考一下,A定义在B之前,为什么输出的结果是BA而不是AB呢? 因为我们在A中加了计时器,在1s后执行console.log语句,B不用计时输出,所以B先输出,A再输出。

当我们想输出AB结果时,我们就可以采用回调函数,看如下代码(对上一段代码的修改)

js 复制代码
function a(callback){    //我们在函数a的定义中传了一个形参callback
  setTimeout(() => {
    console.log("A");
    callback()           //调用了callback(),也就是b()
  },1000)
}
function b(){
  setTimeout(()=>{
    console.log("B")
  })
}
a(b)        //接收一个实参b

这就是加入回调函数之后的代码,这样即使是A需要计时等待,也会输出按AB顺序输出。

回调函数的优缺点

回调函数作为一种处理异步操作的模式,具有一些优点和缺点:

优点:

  1. 灵活性: 回调函数可以让你以一种非阻塞的方式处理异步操作,允许你在操作完成后执行一些特定的逻辑。这种灵活性使得回调函数在处理各种异步场景时非常有用。
  2. 简单直观: 回调函数通常是编写的最基本的异步处理方式之一。对于简单的异步操作,使用回调函数可以保持代码的简洁和直观。
  3. 广泛支持: 回调函数在几乎所有的编程语言和框架中都有支持,因此它是一种通用且可靠的异步编程模式。

缺点:

  1. 回调地狱(Callback Hell):当你有多个嵌套的回调函数时,代码可能会变得难以阅读和维护,这就是所谓的回调地狱。这种情况下,代码会变得深度嵌套,导致可读性差、调试困难和代码重复。
  2. 错误处理困难: 在一些复杂的异步操作中,错误处理可能变得复杂。如果不小心处理错误,可能会导致错误被忽略或无法正确处理。
  3. 难以捕获和传递数据: 在一些情况下,回调函数并不总是方便地处理数据的传递和捕获。特别是在多个回调函数之间需要共享数据时,可能需要引入额外的逻辑来处理数据的传递和共享。
  4. 可读性差: 对于复杂的异步操作,使用回调函数可能会导致代码变得难以理解和维护,因为逻辑分散在多个回调函数中。

现代JavaScript中出现了许多替代方案,例如Promise、async/await等,以解决回调函数带来的一些问题,提高代码的可读性、可维护性和错误处理能力。

promise

由于回调函数的弊端,使得promise以及async await的出现,提高了代码的可读性。

下面利用一段代码来讲解一下,promise是如何使用的:

js 复制代码
function a(){
  return new Promise((resolve,reject)=>{    //return一个实例对象Promise
    setTimeout(() => {
      console.log("A");
      resolve()    //resolve()调用时.then才会生效,回调函数中的b()才会调用
    },1000)
  })
}
function b(){
    console.log("B")
}

a().then(()=>{  //在a()的调用后加 .then(),then是实例对象promise原型上自带的方法
  b()
})

我们首先return一个实例对象Promise,在Promise的回调函数中接收两个形参(resolve,reject),将计时器放入回调函数中,并调用resolve(),当resolve()执行时,结尾的a().then才会执行,其中then回调函数中的b()才会执行,此时"a"以及输出了,"b"才会输出。

此外resolve()中允许传一个实参,在.then回调函数中接收并可以输出:

js 复制代码
function a(){
  return new Promise((resolve,reject)=>{
    setTimeout(() => {
      console.log("A");
      resolve("中国")    //实参:中国
    },1000)
  })
}
function b(){
    console.log("B")
}
a().then((resolve)=>{    //回调函数中拿到resolve的值
  console.log(resolve)  //输出中国
  b()
})

.then的链式调用

看如下代码,连续输出ABC三个字母

js 复制代码
function a(){
  return new Promise((resolve,reject)=>{
    setTimeout(() => {
      console.log("A");       //输出A
      resolve()
    },1000)
  })
}
function b(){
  return new Promise((resolve,reject)=>{
    setTimeout(() => {
    console.log("B");      //输出B
    resolve() 
    },500)
  })
}
function c(){
  console.log("C");     //输出C
}
a()
.then(()=>{     //链式调用
   return b()    //return 出b()中的promise,使得后续能再调用.then输出C
})
.then(()=>{
   c()
})

首先我们调用a(),当执行到a()中的resolve()时,a().then开始生效,其中回调函数中的b()开始生效,输出b,并且return出b()中的promise,使得调用后续的.then输出c,所以输出的顺序时ABC。

如果没有return 出b中的promise,则打印出的结果将会是ACB,因为a().then会自动返回一个promise接收第二个.then,而不是b中的promise接收后续的.then ,我们就不能通过调用resolve()来使得输出B在输出C前,而当我们return b()时,因为b()中return了promise,此个promise就会覆盖掉a().then自动返回的promise,使得后续的.then会在b()return的promise上,就可以先输出B再调用resolve()输出C。

async await

在JavaScripta 中,"async"是"异步"的简写用于声明一个函数是异步的。当一个函数被声明为async时,它可以在函数体内使用"await关键字来等待一个异步操作完成。而"await"关键字用于等待-个Promise对象resolve,并可以将resolve的值赋给一个变量,或者传递给下一个异步操作。

就拿上一段代码来说:

js 复制代码
function a(){
  return new Promise((resolve,reject)=>{
    setTimeout(() => {
      console.log("A");       
      resolve()
    },1000)
  })
}
function b(){
  return new Promise((resolve,reject)=>{
    setTimeout(() => {
    console.log("B");      
    resolve() 
    },500)
  })
}
function c(){
  console.log("C");     
}

async function foo(){      //此处使用async声明函数
    await a()      //await等待异步操作的完成,即resolve()的执行
    await b()
    c()
}
foo()

我们将最后调用阶段的代码使用async await技术,使得我们的代码更加简便,await就相当于.then,await在等待a()和b()中的Promise,若a()和b()中没有return promise,输出则相反CBA。


从回调函数------>promise------>async await,使我们书写代码变得更加简便,代码的可读性也更强!

相关推荐
bysking34 分钟前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓1 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4111 小时前
无网络安装ionic和运行
前端·npm
理想不理想v1 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云1 小时前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
微信:137971205871 小时前
web端手机录音
前端
齐 飞1 小时前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb
神仙别闹1 小时前
基于tensorflow和flask的本地图片库web图片搜索引擎
前端·flask·tensorflow
aPurpleBerry2 小时前
JS常用数组方法 reduce filter find forEach
javascript
GIS程序媛—椰子2 小时前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js