JavaScript中Async/Await通过同步的方式实现异步的方法介绍

 更新时间:2023年06月19日 08:55:52   作者:Cosolar  
在JavaScript的异步编程中,我们经常使用回调函数、Promise和 Async/Await来解决异步操作的问题,Async/Await 又是Promise的语法糖,它的出现让异步编程变得更加直观和易于理解,本文将详细讲解Async/Await如何通过同步的方式实现异步

一、异步编程的问题

在 Web 开发中,我们经常需要进行异步操作,比如从服务器获取数据,或者执行耗时操作。这些任务通常需要一定的时间来完成,而在这段时间内,JavaScript 不能停止执行其他代码,否则会导致界面卡住或者无响应。因此,我们需要使用异步编程技术来处理这些操作。

传统的异步编程技术有回调函数和 Promise。使用回调函数时,我们需要将后续的操作写在回调函数中,这样才能确保在异步操作完成后执行。回调函数虽然简单易用,但是嵌套多个回调函数会导致代码难以阅读和维护。而 Promise 解决了这个问题,它可以链式调用,避免了回调函数嵌套的问题。但是,Promise 的语法并不是那么直观,需要一定的学习成本。

为了更加直观地表达异步代码,ES2017 引入了 Async/Await。Async/Await 可以使异步代码看起来像同步代码,这样可以使代码更加易于理解和维护。接下来,我们将详细讲解 Async/Await 的实现原理。

二、 Async/Await 的实现原理

Async 和 Await 都是异步编程的关键字。在 ES2017 中,Async 函数用来声明一个异步函数,它的定义方式类似于普通的函数,但是在函数关键字前面添加 async 关键字,如下所示:

async function fetchData() {
  // 异步操作
}

我们可以在 Async 函数内部使用 await 关键字来等待异步操作完成。await 表示等待异步操作返回结果后再继续执行后续的代码。

async function fetchData() {
  const result = await fetch("/api/data");
  console.log(result);
}

这段代码中,我们使用了 fetch 函数来获取服务器数据,fetch 返回的是 Promise 实例。我们在该 Promise 实例前面加上 await 关键字,表示等待该 Promise 对象返回数据后再继续执行后续的代码。

当 Async 函数被调用时,它返回的是一个 Promise 对象。Promise 对象有三种状态:已完成、已拒绝和未完成。如果 Async 函数内部没有抛出异常,则该 Promise 对象将进入已完成状态,并返回 Async 函数返回值;如果 Async 函数内部抛出异常,则该 Promise 对象将进入已拒绝状态,并返回抛出的异常。例如,下面这个例子中,Async 函数返回的 Promise 对象的状态为已完成,并返回字符串 "Hello World!":

async function hello() {
  return "Hello World!";
}
hello().then((result) => console.log(result)); // 输出 "Hello World!"

在下面的示例中,我们使用 Async/Await 实现一个简单的异步操作:

async function fetchData() {
  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts");
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}
fetchData();

在这个例子中,我们使用了 fetch 函数来获取服务器数据,并且使用 await 等待数据返回。如果出现异常,则使用 try...catch 处理异常。这段代码使用起来非常直观和易于理解。

Async/Await 的同步实现原理

虽然使用 Async/Await 可以使异步代码看起来像同步代码,但是底层仍然是异步执行的。那么,Async/Await 是如何通过同步的方式实现异步的呢?答案就是 Generator 函数和 Promise。

Generator 函数是一种特殊的函数,它可以被暂停和恢复执行。在 Generator 函数中,我们可以使用 yield 关键字将控制权交给调用方,并在下次调用时从上次暂停的位置继续执行。这种特性可以用来实现异步操作。

Promise 是 ES6 引入的另一种异步编程技术。Promise 对象表示一个尚未完成或失败的操作,它可以被异步执行,并返回一个代表操作结果的值。

Async 函数实际上是一种特殊的 Generator 函数,它使用 yield 关键字暂停执行,并在异步操作完成后,通过调用 next 方法恢复执行。这个过程中,Async 函数内部创建了一个 Promise 对象,并将该 Promise 对象返回给调用方。下面是 Async 函数的简化版实现:

function asyncToGenerator(generatorFunc) {
  return function () {
    const generator = generatorFunc.apply(this, arguments);
    return new Promise((resolve, reject) => {
      function step(key, arg) {
        let generatorResult;
        try {
          generatorResult = generator[key](arg);
        } catch (error) {
          reject(error);
        }
        const { value, done } = generatorResult;
        if (done) {
          resolve(value);
        } else {
          Promise.resolve(value).then(
            (result) => step("next", result),
            (error) => step("throw", error)
          );
        }
      }
      step("next");
    });
  };
}

这段代码中,我们定义了一个名为 asyncToGenerator 的函数,它接收一个 Generator 函数作为参数,并返回一个 Promise 对象。在 asyncToGenerator 函数内部,我们首先调用传入的 Generator 函数,获取到一个迭代器对象。然后,我们在 Promise 对象的构造函数中使用递归调用的方式来处理每一次迭代。如果当前迭代已经完成,则调用 resolve 函数,将结果返回给调用方;否则,将该迭代的 Promise 对象通过 then 方法注册成功和失败回调函数,并在回调函数中继续处理下一次迭代。

三、Async/Await 的使用场景

Async/Await 通常用于处理多个异步操作的情况,它可以避免回调地狱和 Promise 层层嵌套的问题。下面是一个具有挑战性的使用场景。

假设我们需要获取某些商品的信息并计算它们的总价格,其中每个商品需要从服务器获取,并且需要等待前一个商品请求完成后才能发送下一次请求。在写传统异步代码时,我们可能会陷入回调地狱:

function getTotalPrice(items) {
  let totalPrice = 0;
  fetchItem(0, items);
  function fetchItem(index, items) {
    if (index >= items.length) {
      console.log("totalPrice:", totalPrice);
      return;
    }
    const item = items[index];
    fetch(`/api/items/${item.id}`).then((response) => {
      response.json().then((data) => {
        item.price = data.price;
        totalPrice += item.price * item.count;
        fetchItem(index + 1, items);
      });
    });
  }
}

这段代码中,我们首先定义了一个 getTotalPrice 函数,它接收一个商品列表作为参数,并计算所有商品的总价格。我们在该函数中定义了一个名为 fetchItem 的递归函数,用于依次获取每个商品的价格,并累加到 totalPrice 变量中。在 fetchItem 函数中,我们使用 fetch 函数获取商品信息,然后使用嵌套的 Promise.then 调用来等待异步操作返回。这段代码虽然可行,但是非常难以理解和维护。

使用 Async/Await 可以让代码更加直观和易于理解:

async function getTotalPrice(items) {
  let totalPrice = 0;
  for (let item of items) {
    const response = await fetch(`/api/items/${item.id}`);
    const data = await response.json();
    item.price = data.price;
    totalPrice += item.price * item.count;
  }
  console.log("totalPrice:", totalPrice);
}

可以看到,在上面的代码中,我们使用了 Async/Await 和 for...of 循环,避免了回调地狱和 Promise 层层嵌套的问题。这样的代码看起来非常简单和直观。

四、小结一下

Async/Await 是一种比较新的异步编程技术,它使异步代码看起来像同步代码,更加直观和易于理解。Async/Await 的实现原理是 Generator 函数和 Promise,它通过同步的方式实现异步。使用 Async/Await 可以避免回调地狱和 Promise 层层嵌套的问题。Async/Await 通常用于处理多个异步操作的情况,这样的代码看起来非常简单和直观。

以上就是JavaScript中Async/Await通过同步的方式实现异步的方法介绍的详细内容,更多关于JavaScript Async/Await异步的资料请关注脚本之家其它相关文章!

相关文章

  • 纯javascript实现简单下拉刷新功能

    纯javascript实现简单下拉刷新功能

    这篇文章主要介绍了纯javascript实现简单下拉刷新功能,没有借助任何的框架,十分简单实用,有需要的小伙伴来参考下吧。
    2015-03-03
  • 在Html中使用Requirejs进行模块化开发实例详解

    在Html中使用Requirejs进行模块化开发实例详解

    在前端模块化的时候,不仅仅是js需要进行模块化管理,html有时候也需要模块化管理。这里就介绍下如何通过requirejs,实现html代码的模块化开发
    2016-04-04
  • TypeScript前端数据校验c快速上手指南

    TypeScript前端数据校验c快速上手指南

    Zod是一个以TypeScript为先的模式验证库,具有静态类型推断功能,它旨在提供强大的运行时验证,同时充分利用TypeScript的类型系统,这篇文章主要介绍了TypeScript前端数据校验c快速上手的相关资料,需要的朋友可以参考下
    2026-05-05
  • javascript实现富文本框选中对齐的思路与代码

    javascript实现富文本框选中对齐的思路与代码

    最近在项目中经常遇到使用富文本框的情况,下面这篇文章主要给大家介绍了关于javascript实现富文本框选中对齐的思路与代码,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-03-03
  • JS+CSS 制作的超级简单的下拉菜单附图

    JS+CSS 制作的超级简单的下拉菜单附图

    下拉菜单想必大家都有见到过吧,在本文将为大家介绍个不错的示例,超简单的,大家可以参考下哦
    2013-11-11
  • JS实现表格响应式布局技巧

    JS实现表格响应式布局技巧

    这篇文章主要为大家介绍了JS实现表格响应式布局技巧示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • 原生JS+Canvas实现五子棋游戏

    原生JS+Canvas实现五子棋游戏

    这篇文章主要为大家详细介绍了原生JS+Canvas实现五子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • js实现从左向右滑动式轮播图效果

    js实现从左向右滑动式轮播图效果

    这篇文章主要为大家详细介绍了js实现从左向右滑动式轮播图效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • js动态添加删除,后台取数据(示例代码)

    js动态添加删除,后台取数据(示例代码)

    这篇文章主要是对js动态添加删除,后台取数据(示例代码)进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助
    2013-11-11
  • 简介BootStrap model弹出框的使用

    简介BootStrap model弹出框的使用

    这篇文章主要介绍了BootStrap model弹出框的使用,介绍的非常详细,具有参考借鉴价值,感兴趣的朋友一起学习吧
    2016-04-04

最新评论