Async/Await : implémentez l'asynchronisme avec élégance et concision grâce à la synchronisation

Dans la programmation asynchrone JavaScript, nous utilisons souvent la fonction de rappel, Promise et Async/Await pour résoudre le problème du fonctionnement asynchrone. Et Async/Await est le sucre syntaxique de Promise, son apparence rend la programmation asynchrone plus intuitive et facile à comprendre. Cet article explique en détail comment Async/Await réalise l'asynchronisme grâce à la synchronisation.

1. Le problème de la programmation asynchrone

Dans le développement Web, nous devons souvent effectuer des opérations asynchrones, telles que la récupération de données à partir d'un serveur ou des opérations chronophages. Ces tâches prennent généralement un certain temps pour s'achever, et pendant ce temps, JavaScript ne peut pas arrêter d'exécuter d'autres codes, sinon l'interface se bloquera ou ne répondra plus. Par conséquent, nous devons utiliser des techniques de programmation asynchrone pour gérer ces opérations.

Les techniques traditionnelles de programmation asynchrone sont les fonctions de rappel et les promesses. Lors de l'utilisation de la fonction de rappel, nous devons écrire les opérations suivantes dans la fonction de rappel, afin de garantir que l'opération asynchrone est exécutée une fois terminée. Bien que la fonction de rappel soit simple et facile à utiliser, l'imbrication de plusieurs fonctions de rappel rendra le code difficile à lire et à maintenir. Et Promise résout ce problème, il peut être appelé dans une chaîne, évitant le problème de l'imbrication des fonctions de rappel. Cependant, la syntaxe de Promise n'est pas si intuitive et demande un certain coût d'apprentissage.

Afin d'exprimer le code asynchrone de manière plus intuitive, ES2017 introduit Async/Await. Async/Await peut faire ressembler le code asynchrone à du code synchrone, ce qui peut faciliter la compréhension et la maintenance du code. Ensuite, nous expliquerons en détail le principe de mise en œuvre d'Async/Await.

2. Le principe de réalisation d'Async/Await

Async et Await sont les deux mots clés de la programmation asynchrone. Dans ES2017, la fonction Async est utilisée pour déclarer une fonction asynchrone, qui est définie de la même manière qu'une fonction normale, mais le mot clé async est ajouté devant le mot clé function, comme indiqué ci-dessous :

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

Nous pouvons utiliser le mot clé await dans une fonction Async pour attendre la fin d'une opération asynchrone. attendre signifie attendre que le résultat de l'opération asynchrone revienne avant de continuer à exécuter le code suivant.

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 通常用于处理多个异步操作的情况,这样的代码看起来非常简单和直观。

Je suppose que tu aimes

Origine juejin.im/post/7245922875182170172
conseillé
Classement