"深入探究 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,使我们书写代码变得更加简便,代码的可读性也更强!

相关推荐
一丝晨光23 分钟前
C++、Ruby和JavaScript
java·开发语言·javascript·c++·python·c·ruby
Front思25 分钟前
vue使用高德地图
javascript·vue.js·ecmascript
zqx_71 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己1 小时前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称2 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色2 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2342 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js
秋殇与星河2 小时前
CSS总结
前端·css
NiNg_1_2342 小时前
Vue3 Pinia持久化存储
开发语言·javascript·ecmascript
读心悦2 小时前
如何在 Axios 中封装事件中心EventEmitter
javascript·http