JS之async、await详解

async和await的基础使用

async/awiat的使用规则:

  • async 表示这是一个async函数, await只能用在async函数里面,不能单独使用
  • async 返回的是一个Promise对象,await就是等待这个promise的返回结果后,再继续执行
  • await 等待的是一个Promise对象,后面必须跟一个Promise对象,但是不必写then(),直接就可以得到返回值

async/await的特点

  • Async作为关键字放在函数前面,普通函数变成了异步函数
  • 异步函数async函数调用,跟普通函数调用方式一样。在一个函数前面加上async,变成 async函数,异步函数,return:1,打印返回值,
  • 返回的是
    promise成功的对象
  • Async函数配合await关键字使用

案例

模拟出一个摇色子的异步操作,先通过一个方法三秒之后拿到一个筛子数,第二步进行输出

//基本用法的async函数
let asyncFun = async function(){
    return 1
}
console.log(asyncFun())
//会返回一个promise对象


//使用场景
//摇色子方法
function dice(){
    return new Promise((resolve,reject)=>{
        let sino = parseInt(Math.random()*6+1)  //生成一个1~6之间的随机小数
        setTimeout(()=>{
            resolve(sino)
        },2000)
    })
}
//异步方法
 async function text(){
     let n= await dice()
      //await 关键字后面调用摇色子方法执行完毕之后,才进行变量赋值
     console.log("摇出来"+n)  //最后打印出摇出来的数
 }
text()

上面这段代码async中使await 摇色子()先执行,等到三秒后执行完再把得到的结果赋值给左边的n,也就是说test函数需要三秒钟才执行完成,所以test函数是异步的,因此前面必须写async 

async/await的优点

  1. 方便级联调用:即调用依次发生的场景;
  2. 同步代码编写方式:Promise使用then函数进行链式调用,一直点点点,是一种从左向右的横向写法;async/await从上到下,顺序执行,就像写同步代码一样,更符合代码编写习惯;
  3. 多个参数传递:Promise的then函数只能传递一个参数,虽然可以通过包装成对象来传递多个参数,但是会导致传递冗余信息,频繁的解析又重新组合参数,比较麻烦;async/await没有这个限制,可以当做普通的局部变量来处理,用let或者const定义的块级变量想怎么用就怎么用,想定义几个就定义几个,完全没有限制,也没有冗余工作;

  4. 同步代码和异步代码可以一起编写:使用Promise的时候最好将同步代码和异步代码放在不同的then节点中,这样结构更加清晰;async/await整个书写习惯都是同步的,不需要纠结同步和异步的区别,当然,异步过程需要包装成一个Promise对象放在await关键字后面;
  5. 基于协程:Promise是根据函数式编程的范式,对异步过程进行了一层封装,async/await基于协程的机制,是真正的“保存上下文,控制权切换……控制权恢复,取回上下文”这种机制,是对异步过程更精确的一种描述;
  6. async/await是对Promise的优化:async/await是基于Promise的,是进一步的一种优化,不过在写代码时,Promise本身的API出现得很少,很接近同步代码的写法

使用场景

async主要来处理异步的操作,
需求:执行第一步,将执行第一步的结果返回给第二步使用。在ajax中先拿到一个接口的返回数据,后使用第一部返回的数据执行第二步操作的接口调用,达到异步操作。

Vue项目案例

普通案例promise:

methods: {
     getLocation(phoneNum) {
         return axios.post('/one接口', {
             phoneNum
         })
     },    
     getFaceList(province, city) {
         return axios.post('/two接口', {
             province,
             city
         })
     },  
     getFaceResult () {
          this.getLocation(this.phoneNum).then(res => {
              if (res.status === 200 && res.data.success) {
              let province = res.data.obj.province;
              let city = res.data.obj.city;
                  this.getFaceList(province, city).then(res => {
                        if(res.status === 200 && res.data.success) {
                             this.faceList = res.data.obj
                        }
                  })
              }
         }).catch(err => {
             console.log(err)
         })     
     }
}

这时你看到了then 的链式写法,有一点回调地域的感觉。现在我们在有async/ await 来改造一下。

async/ await案例:

首先把 getFaceResult 转化成一个async 函数,就是在其前面加async, 因为它的调用方法和普通函数的调用方法是一致,所以没有什么问题。然后就把 getLocation 和getFaceList 放到await 后面,等待执行, getFaceResult 函数修改如下:

async getFaceResult () {
    let location = await this.getLocation(this.phoneNum);
    if (location.data.success) {
        let province = location.data.obj.province;
        let city = location.data.obj.city;
        let result = await this.getFaceList(province, city);
        if (result.data.success) {
            this.faceList = result.data.obj;
        }
    }
 }

现在代码的书写方式,就像写同步代码一样,没有回调的感觉,非常舒服。

现在就还差一点需要说明,那就是怎么处理异常,如果请求发生异常,怎么处理? 它用的是try/catch 来捕获异常把await 放到 try 中进行执行,如有异常,就使用catch 进行处理

async getFaceResult () {
    try {
        let location = await this.getLocation(this.phoneNum);
        if (location.data.success) {
            let province = location.data.obj.province;
            let city = location.data.obj.city;
            let result = await this.getFaceList(province, city);
            if (result.data.success) {
                this.faceList = result.data.obj;
            }
        }
    } catch(err) {
        console.log(err);
    }
}

 Vue项目案例(封装)

http.js

'use strict'
 
import axios from 'axios'
import qs from 'qs'
 
axios.interceptors.request.use(config => {
  // loading
  return config
}, error => {
  return Promise.reject(error)
})
 
axios.interceptors.response.use(response => {
  return response
}, error => {
  return Promise.resolve(error.response)
})
 
function checkStatus (response) {
  // loading
  // 如果http状态码正常,则直接返回数据
  if (response && (response.status === 200 || response.status === 304 || response.status === 400)) {
    return response
    // 如果不需要除了data之外的数据,可以直接 return response.data
  }
  // 异常状态下,把错误信息返回去
  return {
    status: -404,
    msg: '网络异常'
  }
}
 
function checkCode (res) {
  // 如果code异常(这里已经包括网络错误,服务器错误,后端抛出的错误),可以弹出一个错误提示,告诉用户
  if (res.status === -404) {
    alert(res.msg)
  }
  if (res.data && (!res.data.success)) {
    alert(res.data.error_msg)
  }
  return res
}
 
export default {
  post (data,url) {
    return axios({
      method: 'post',
      url: url,
      data: qs.stringify(data),
      timeout: 10000,
      headers: {
        'X-Requested-With': 'XMLHttpRequest',
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
      }
    }).then(
      (response) => {
        return checkStatus(response)
      }
    )
  },
  get (url, params) {
    return axios({
      method: 'get',
      baseURL: 'https://cnodejs.org/api/v1',
      url,
      params, // get 请求时带的参数
      timeout: 10000,
      headers: {
        'X-Requested-With': 'XMLHttpRequest'
      }
    }).then(
      (response) => {
        return checkStatus(response)
      }
    ).then(
      (res) => {
        return checkCode(res)
      }
    )
  }
}

api.js

export default {
  getCode: 'http://127.0.0.1:8888/.....'
}

auth.vue

import http from '../../utils/http'
import api from '../../utils/api'
   methods: {
      fetchData: async function () {
          var that = this
          var code = Store.fetchYqm();
          let params = {
              inviteCode: code
          }
          const response = await http.post(params,api.getCode)
          var resJson = response.data;
      }
}

<think>我们被要求详细解释JavaScript中的asyncawait的用法和原理。根据用户提供的引用资料,我们可以总结如下: 1. async函数和await关键字是ES2017引入的,基于Promise的语法糖,使异步代码更易写和读。 2. async函数声明一个异步函数,该函数会返回一个Promise对象。如果函数返回一个值,该值会被包装为Promise.resolve(返回值);如果抛出异常,则返回Promise.reject(异常)。 3. await关键字只能在async函数内部使用,用于等待一个Promise对象的状态变为resolved,并返回其解决的值。如果等待的不是Promise,则直接返回该值。 4. async函数内部,await会暂停函数的执行,直到等待的Promise解决,然后继续执行函数并返回结果。注意:async函数不会阻塞主线程,它只是暂停函数内部的执行。 下面我们详细展开。 ### 用法 - **async函数声明**: ```javascript async function myFunc() { // 函数体 } ``` 或箭头函数: ```javascript const myFunc = async () => { // 函数体 }; ``` - **await表达式**:在async函数中,可以等待一个Promise完成: ```javascript async function example() { const result = await someAsyncOperation(); // 等待异步操作完成 console.log(result); } ``` ### 原理 async/await实际上是Generator函数的语法糖,它利用了Generator函数的暂停执行和恢复执行的能力,但不需要自己编写执行器(如co模块),因为async函数内置了执行器。 具体来说,当调用一个async函数时,它会返回一个Promise。当async函数执行时,一旦遇到await就会暂停执行,将后面的代码放到微任务队列中,然后跳出函数继续执行后续代码。等到await后面的表达式(通常是一个Promise)状态变为resolved,JavaScript引擎会从暂停的地方恢复执行async函数。 ### 错误处理 在async函数中,可以使用try...catch来捕获异常: ```javascript async function fetchData() { try { const response = await fetch('https://api.example.com/data'); const data = await response.json(); return data; } catch (error) { console.error('Error:', error); } } ``` ### 示例 以下是一个使用async/await的简单示例: ```javascript function resolveAfter2Seconds() { return new Promise(resolve => { setTimeout(() => { resolve('resolved'); }, 2000); }); } async function asyncCall() { console.log('calling'); const result = await resolveAfter2Seconds(); console.log(result); // 2秒后打印 'resolved' return result; } asyncCall().then(res => console.log('then:', res)); // 最后打印 'then: resolved' ``` ### 注意事项 1. 不能在普通函数中使用await,否则会报语法错误。 2. async函数返回的Promise对象,必须等到内部所有await命令后面的Promise对象执行完,才会发生状态改变(除非遇到return语句或者抛出错误)。也就是说,async函数内部所有的异步操作执行完,才会执行then方法指定的回调函数。 3. 多个await命令后面的异步操作,如果不存在依赖关系,最好同时触发(使用Promise.all)以提高效率。 ```javascript async function parallel() { const [result1, result2] = await Promise.all([asyncTask1(), asyncTask2()]); // ... } ``` ### 与Promise对比 async/await使得异步代码的书写更加线性,避免了Promise链式调用中的多层嵌套,提高了代码的可读性。 例如,使用Promise: ```javascript function getJSON() { return fetch('data.json') .then(response => response.json()) .then(json => console.log(json)) .catch(error => console.log('error:', error)); } ``` 使用async/await: ```javascript async function getJSON() { try { const response = await fetch('data.json'); const json = await response.json(); console.log(json); } catch (error) { console.log('error:', error); } } ``` ### 引用说明 根据用户提供的引用: - asyncawaitJavaScript提供了强大的异步处理能力,使代码更具可读性和可维护性[^1]。 - 它们是基于Promise的语法糖,使异步代码更易于编写和阅读[^2]。 - async函数被调用不会阻塞界面渲染,内部由await关键字修饰异步过程,会阻塞等待异步任务的完成再返回[^3]。 §§相关问题§§ 1. async函数和普通函数有什么区别? 2. 在async函数中,如何同时处理多个异步任务? 3. async/await如何处理错误? 4. 为什么说async/await是基于Promise的? 5. 在哪些场景下使用async/await比使用Promise更合适?</think>### JavaScript async/await 详解 #### 一、基本概念 async/await 是 ES2017 引入的异步编程解决方案,基于 Promise 的语法糖,使异步代码具有同步代码的可读性。 **核心特点**: 1. `async` 声明异步函数,始终返回 Promise 对象 ```javascript async function fetchData() { return "data"; // 等价于 Promise.resolve("data") } ``` 2. `await` 暂停异步函数执行,等待 Promise 完成 ```javascript async function getUser() { const response = await fetch('/api/user'); // 等待请求完成 return response.json(); } ``` #### 二、工作原理 1. **执行流程**: - 遇到 `await` 时暂停函数执行,将控制权交还事件循环 - 等待 Promise 完成后恢复函数执行 - 不会阻塞主线程(非 CPU 阻塞)[^3] 2. **错误处理**: ```javascript async function fetchWithRetry() { try { const data = await fetchData(); } catch (error) { console.error("请求失败:", error); } } ``` 3. **返回值转换**: | 返回值类型 | 转换结果 | |------------------|--------------------------| | 非 Promise 值 | `Promise.resolve(value)` | | Promise 对象 | 直接返回该 Promise | | 抛出错误 | `Promise.reject(error)` | #### 三、关键特性 1. **执行顺序示例**: ```javascript console.log("Start"); async function demo() { console.log("Async start"); await new Promise(r => setTimeout(r, 1000)); console.log("Async end"); } demo(); console.log("End"); // 输出顺序: // Start → Async start → End → (1秒后) Async end ``` 2. **并行执行优化**: ```javascript // 顺序执行 (慢) const user = await fetchUser(); const posts = await fetchPosts(); // 并行执行 (快) const [user, posts] = await Promise.all([ fetchUser(), fetchPosts() ]); ``` 3. **顶层 await** (ES2022): ```javascript // 模块中直接使用 const data = await fetchConfig(); export default data; ``` #### 四、使用场景 1. 顺序异步操作(如 API 链式调用) 2. 错误处理复杂的异步流程 3. 替代 Promise 链式调用: ```javascript // Promise 版本 fetchData() .then(process) .then(save) .catch(handleError); // async/await 版本 try { const data = await fetchData(); const processed = await process(data); await save(processed); } catch (err) { handleError(err); } ``` #### 五、注意事项 1. **避免阻塞陷阱**: ```javascript // 错误示例 (意外阻塞) async function processBatch() { items.forEach(async item => { await processItem(item); // 不会并行执行 }); } // 正确做法 async function processBatch() { await Promise.all(items.map(processItem)); } ``` 2. **浏览器兼容性**: - 现代浏览器和 Node.js 12+ 完全支持 - 旧环境需通过 Babel 转译 3. **性能影响**: - 比原生 Promise 稍慢(约 10%) - 可读性优势通常大于性能差异 > async/await 让异步代码获得同步代码的可读性,同时保持非阻塞特性,是现代 JavaScript 异步编程的首选方案[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值