探究 ES6 生成器 ( Generator ) 的异步编程运用

一. 前言

在之前的文章中,咱们介绍了生成器函数的基本概念和常见运用,包含异步操作的次序履行、操控异步流程等,一起也了解到 Promise 和生成器结合的运用可以协助咱们更方便地处理异步操作。详细了解请参阅之前的文章:

学习 ES6 生成器 ( Generator ) :把握优雅的异步编程利器

然而,生成器函数的运用不只限于此,它还有一些比较高档的运用,可以用于完结并发操控,运用生成器完结可撤销的异步操作等等,以进步代码的功能和效率。在本文中,咱们将介绍生成器的这些高档运用。

在介绍生成器的运用之前,咱们先介绍几种生成器中的过错处理办法,在生成器中处理过错也是一种重要的机制,可以有效地捕获和处理异步操作中的过错。在接下来的异步编程运用中,咱们会常常会用到怎么处理反常信息。

二. 生成器中的过错处理

1. try…catch 句子

在生成器函数中处理过错可以借助于try...catch句子。经过在生成器函数中运用try...catch句子,可以捕获异步操作中的过错并采纳相应的处理办法。

下面是一个示例,展现了在生成器函数中处理过错的办法:

function* asyncGenerator() {
  try {
    const result = yield asyncOperation(); // 进行异步操作
    console.log("异步操作成果:", result);
  } catch (error) {
    console.error("产生过错:", error);
  }
}
function asyncOperation() {
  return new Promise((resolve, reject) => {
    // 模仿异步操作
    setTimeout(() => {
      const success = Math.random() >= 0.5;
      if (success) {
        resolve("成功成果");
      } else {
        reject(new Error("操作失利"));
      }
    }, 1000);
  });
}
const asyncFlow = asyncGenerator();
const { value, done } = asyncFlow.next();
if (!done) {
  value
    .then((data) => {
      asyncFlow.next(data);
    })
    .catch((error) => {
      asyncFlow.throw(error);
    });
}

在上述示例中,咱们界说了一个生成器函数asyncGenerator,其间运用了try...catch句子来捕获可能产生的异步操作内的过错。

asyncGenerator函数中,咱们经过yield关键字将异步操作asyncOperation进行了挂起。当异步操作完结后,将成果传递给生成器函数的yield句子。

在外部,咱们经过调用asyncGenerator函数并获取生成器目标后,运用next()办法将生成器函数的履行推动一步。然后,咱们经过判断生成器目标的done特点来判断是否一切进程都已完结。假如未完结,咱们依据value特点是Promise目标仍是过错目标来处理下一步是next()仍是throw()

在上述示例中,asyncOperation函数是一个模仿异步操作的函数,它回来一个Promise目标,模仿异步操作的成功或失利。在异步操作中,resolve被调用时会传递成功成果,reject被调用时会传递过错信息。

履行成果如下图所示:

探究 ES6 生成器 ( Generator ) 的异步编程运用

经过在生成器函数中运用try...catch句子,咱们可以捕获异步操作中可能产生的过错,并在catch块中进行相应的处理。

请留意,过错处理是完结稳定的异步代码非常重要的一部分。在实际开发中,你可能还需求结合特定的异步操控库来处理过错,以保证过错可以被正确捕获和处理。

2. throw 办法

在生成器函数中,可以运用throw()办法来抛出一个过错,并使生成器函数在相应的catch句子中暂停履行。

下面是一个示例,展现了运用生成器函数的throw()办法和过错处理的办法:

function* generatorFunction() {
  try {
    yield "Step 1";
    yield "Step 2";
    throw new Error("Something went wrong"); // 抛出过错
    yield "Step 3"; // 不会履行到这儿
  } catch (error) {
    console.log("Error:", error.message);
    yield "Step 4";
  }
}
const generator = generatorFunction();
console.log(generator.next().value); // Output: Step 1
console.log(generator.next().value); // Output: Step 2
console.log(generator.throw(new Error("Another error message")).value); // Output: Error: Another error message
console.log(generator.next().value); // Output: Step 4

在上述示例中,咱们界说了一个生成器函数generatorFunction,其间经过yield句子界说了一系列的进程。

generatorFunction中,咱们运用了try...catch句子来捕获在生成器函数中可能产生的过错。当抛出过错时,生成器函数会当即中止履行,并跳转到相应的catch句子中处理过错。在catch句子中,咱们可以对捕获的过错进行相应的处理。

在外部,咱们首要创立了生成器目标generator,然后经过next()办法来逐步履行生成器函数中的进程。在第一次调用next()之后,生成器函数会履行到第一个yield句子,并回来相应的值。

在第2次调用next()之后,生成器函数会持续履行到第二个yield句子,并回来相应的值。

接着,咱们运用throw()办法抛出了一个新的过错,并将过错目标作为参数传递给throw()办法。生成器函数会当即中止履行,并跳转到相应的catch句子中处理过错。咱们可以在catch句子中对捕获的过错进行相应的处理,并回来相应的值。

在上述示例中,生成器函数在抛出过错后,不会再履行剩余的进程。经过运用throw()办法和相应的过错处理机制,咱们可以更好地操控生成器函数的履行流程,并及时处理产生的过错。

履行的成果如下图所示:

探究 ES6 生成器 ( Generator ) 的异步编程运用

3. 运用 Promise 过错处理机制

在生成器中,可以经过回来 reject 状况的 Promise 来处理过错。这种办法非常适合处理异步操作中的过错。下面是详细的阐明:

function asyncOperation() {
  return new Promise((resolve, reject) => {
    // 异步操作
    setTimeout(() => {
      // 模仿履行过错操作
      const error = new Error("An error occurred");
      reject(error);
      // 模仿履行成功操作
      // resolve('Async operation completed');
    }, 2000);
  });
}
function* myGenerator() {
  try {
    const result = yield asyncOperation();
    console.log("Result:", result);
  } catch (error) {
    console.error("Error:", error);
  }
}
const generator = myGenerator();
function handlePromise(result) {
  generator.next(result);
}
function handleError(error) {
  generator.throw(error);
}
generator.next().value.then(handlePromise).catch(handleError);

在上面的代码中,asyncOperation函数回来一个 Promise 目标,模仿一个异步操作,延迟 2 秒后会进入 reject 状况。在myGenerator生成器函数中,咱们运用yield asyncOperation()来暂停生成器,并回来 Promise 目标。经过try-catch句子块,咱们可以对过错进行捕获和处理。假如 Promise 目标进入了 reject 状况,过错会被抛到catch块中进行处理。

在主程序中,经过调用生成器的next()办法,发动生成器的履行。在 Promise 的then办法中,调用handlePromise函数将成果传递给生成器持续履行。假如 Promise 进入了 reject 状况,过错会被捕获并抛出,然后触产生成器的catch块。

探究 ES6 生成器 ( Generator ) 的异步编程运用

经过这种办法,咱们可以在生成器中完结过错处理逻辑,将异步操作的过错捕获和处理放在生成器内部,使得代码愈加简洁和易于阅览。一起,结合 Promise 的过错处理机制,可以保证异步操作的过错可以得到正确地处理和传递。

需求留意的是,在处理 Promise 目标时,可以运用 .then 办法获取异步操作的成果,并经过 generator.next()办法将成果回来给生成器。假如 Promise 进入 reject 状况,可以经过 .catch 办法捕获过错并处理。经过这种办法,可以在异步操作中完结过错处理的流程操控。

总归,经过回来 reject 状况的 Promise,可以在生成器中有效地处理异步操作中的过错,并完结过错处理的流程操控。这种办法可以使生成器愈加灵敏和牢靠。

三. 生成器的异步编程运用

1. 与 async/await 结合运用

生成器函数和async/await可以很好地结合运用,以简化异步编程,并使代码更具可读性和可维护性。下面是运用生成器和async/await形式处理异步操作的示例:

function getData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("数据");
    }, 1000);
  });
}
function* fetchAsyncData() {
  try {
    const data = yield getData();
    console.log("获取到的数据:", data);
    const moreData = yield getData();
    console.log("更多的数据:", moreData);
    return "完结";
  } catch (error) {
    console.error("产生过错:", error);
    return "犯错";
  }
}
async function fetchData() {
  try {
    const generator = fetchAsyncData();
    let result = generator.next();
    while (!result.done) {
      result = await result.value;
      result = generator.next(result);
    }
    console.log("终究成果:", result.value);
  } catch (error) {
    console.error("产生过错:", error);
  }
}
fetchData();

在上面的示例中,fetchAsyncData是一个生成器函数,用于处理异步操作。在生成器函数中,咱们运用yield关键字暂停履行,并经过yield关键字回来一个Promise目标,此处运用了getData函数模仿异步操作。

fetchData函数中,咱们运用async/await形式来驱动生成器函数的履行。在while循环中,咱们运用await关键字等待异步操作的成果,并经过generator.next()办法将成果传递给生成器函数。

探究 ES6 生成器 ( Generator ) 的异步编程运用

生成器函数中的try...catch块可以捕获异步操作中的反常状况,保证代码的稳定性。在catch块中,咱们可以处理过错并回来成果经过结合运用生成器和async/await式,咱们可以以次序和分步的办法处理异步操作,使代码更清晰、易读,而且可以很好地处理反常状况。

2. 完结可撤销的异步操作

运用生成器完结可撤销的异步操作可以经过结合运用生成器函数和yield句子来完结暂停和康复异步使命。下面是一个简略的示例:

function* cancellableAsyncOperation() {
  try {
    const result = yield new Promise((resolve, reject) => {
      const timeoutId = setTimeout(() => {
        resolve("操作完结");
      }, 3000);
      // 注册撤销操作的回调函数
      onCancel(() => {
        clearTimeout(timeoutId);
        reject(new Error("操作被撤销"));
      });
    });
    console.log(result);
  } catch (err) {
    console.error("操作犯错:", err.message);
  }
}
// 撤销操作的函数
let cancelCallback = null;
function onCancel(callback) {
  cancelCallback = callback;
}
// 撤销操作的函数调用
function cancelOperation() {
  if (cancelCallback) {
    cancelCallback();
  }
}
// 履行异步操作
const iterator = cancellableAsyncOperation();
const promise = iterator.next().value;
promise
  .then(() => {
    console.log("操作完结");
  })
  .catch((err) => {
    console.error("操作被撤销或犯错:", err.message);
  });
// 5秒后撤销操作
setTimeout(() => {
  console.log("撤销操作");
  cancelOperation();
}, 5000);

在这个示例中,咱们界说了一个生成器函数cancellableAsyncOperation,它模仿一个异步操作的履行进程。在生成器函数内部,咱们创立了一个Promise目标,并在其间注册了一个定时器来模仿异步操作的延时履行。一起,咱们经过onCancel函数,注册了一个撤销操作的回调函数。

然后,咱们界说了一个全局的cancelCallback变量,用来保存撤销操作的回调函数。

接着,咱们经过调用iterator.next().value来获取生成器的第一个yield表达式回来的Promise目标,并经过then()catch()办法处理操作的完结和撤销或犯错的状况。

最终,咱们经过setTimeout函数,在 5 秒后调用cancelOperation函数来撤销操作。cancelOperation函数会调用之前注册的撤销操作的回调函数,然后中止异步操作的履行。

履行流程如下图所示:

需求留意的是,这个示例仅仅模仿了可撤销的异步操作的完结办法。在实际运用中,可能需求依据详细需求和异步操作的性质,进行恰当的调整和优化

3. 完结并发操控

在编程中,咱们经常需求处理并行履行的使命,例如一起下载多个文件,或许一起发送多个请求。并发操控便是一种办理并行使命履行的机制,它可以操控使命的数量、次序和成果处理等。

基本原理及进程

生成器函数提供了一种优雅且灵敏的办法来完结并发操控。经过运用生成器函数和 yield 表达式,咱们可以轻松地办理多个并发使命的履行,以及处理使命的成果。下面是生成器函数并发操控的基本原理:

  • 创立一个生成器函数,该函数包含多个异步使命的生成器调用,并将每个使命包装在 yield 表达式中。
  • 经过操控生成器的迭代进程,以及运用异步操作的 Promise 成果,完结使命的并发履行和成果处理。

因而,生成器的并发操控可以经过运用异步操作的 Promise 和生成器函数的特性来完结。下面是一个简略的示例,咱们看一下怎么运用生成器函数来办理并发使命的履行:

// 模仿异步请求
function fetch(url) {
  return new Promise((resolve, reject) => {
    console.log("异步操作履行中:"   url);
    const timer = setTimeout(() => {
      clearTimeout(timer);
      console.log("异步操作完结:"   url);
      resolve({ status: 1, msg: "异步操作完结:"   url, data: null });
    }, 2000);
  });
}
// 生成器函数
function* taskGenerator() {
  const task1 = yield fetch("https://api.example.com/task1");
  const task2 = yield fetch("https://api.example.com/task2");
  const task3 = yield fetch("https://api.example.com/task3");
  console.log(task1);
  console.log(task2);
  console.log(task3);
}
function executeGenerator(generator) {
  const iterator = generator();
  function handle(iteratorResult) {
    if (iteratorResult.done) {
      return Promise.resolve(iteratorResult.value);
    }
    return Promise.resolve(iteratorResult.value)
      .then((res) => handle(iterator.next(res)))
      .catch((err) => iterator.throw(err));
  }
  try {
    return handle(iterator.next());
  } catch (err) {
    return Promise.reject(err);
  }
}
// 履行使命
executeGenerator(taskGenerator)
  .then(() => {
    console.log("一切使命完结");
  })
  .catch((err) => {
    console.error("履行进程中犯错:", err);
  });

在上面这个示例中,咱们界说了一个生成器函数taskGenerator,它包含三个异步使命。每个使命运用yield句子暂停生成器的履行,等待相应的异步操作成果。

然后,咱们界说了一个辅助函数executeGenerator,用于履行生成器。在函数内部,咱们经过递归调用handle函数,顺次处理生成器的每个yield表达式。其间,handle函数接收一个yield表达式的成果,并依据成果持续履行生成器的下一个yield表达式,直到生成器履行完毕。

最终,咱们调用executeGenerator(taskGenerator)来履行生成器。在生成器履行进程中,每个使命会并发地履行。当一切使命都完结后,输出“一切使命完结”。假如在履行进程中呈现过错,会捕获并输出过错信息。

履行流程如下图所示:

需求留意的是,生成器的并发操控是经过次序履行使命并处理它们的成果来完结的,并非真实的并行履行。假如需求更高档的并行操控,可以结合运用生成器和其他异步编程形式,例如运用Promise.all()来一起履行多个使命。

经过以上的代码演示,咱们可以完结并发使命的履行和成果处理,有效地运用生成器的并发操控才能。当然,这仅仅一个简略的示例,你可以依据实际需求进行更杂乱的并发操控完结。

4. 嵌套和组合

生成器的嵌套和组合是指在生成器函数中运用其他生成器函数的才能,以及将多个生成器函数组合在一起以构成更杂乱的生成器函数。

嵌套其他生成器函数

在生成器函数中运用其他生成器函数可以让咱们更好地安排和复用代码。咱们可以将一部分逻辑封装在一个生成器函数中,然后在另一个生成器函数中经过 yield* 语法来调用这个生成器函数。这样做可以减少代码的重复性,进步代码的可读性和可维护性。

下面是一个简略的比如,展现了怎么在生成器函数中嵌套运用其他生成器函数:

function* innerGenerator() {
  yield 1;
  yield 2;
  yield 3;
}
function* outerGenerator() {
  yield "a";
  yield "b";
  yield "c";
  yield* innerGenerator();
  yield "d";
  yield "e";
}
const generator = outerGenerator();
for (const value of generator) {
  console.log(value);
}

在上面的比如中,innerGenerator 是一个简略的生成器函数,它生成了数字 1、2、3。outerGenerator 是另一个生成器函数,它先生成字母 ‘a’、’b’、’c’,然后经过 yield* 调用了 innerGenerator,生成了数字 1、2、3。最终,它生成了字母 ‘d’、’e’。当咱们遍历 outerGenerator 的回来值时,会顺次输出 ‘a’、’b’、’c’、1、2、3、’d’、’e’。

探究 ES6 生成器 ( Generator ) 的异步编程运用

组合多个生成器函数

除了嵌套生成器函数,咱们还可以经过组合多个生成器函数来创立更杂乱的生成器函数。这样做可以将不同的生成器函数的逻辑组合在一起,构成一个新的生成器函数。在组合生成器函数时,可以运用 yield* 或经过遍历多个生成器函数的回来值来完结。

下面是一个示例,演示了怎么组合多个生成器函数:

function* generator1() {
  yield "A";
  yield "B";
}
function* generator2() {
  yield "1";
  yield "2";
}
function* combinedGenerator() {
  yield* generator1();
  yield* generator2();
}
const generator = combinedGenerator();
for (const value of generator) {
  console.log(value);
}

在上面的比如中,generator1generator2 分别是两个简略的生成器函数,分别生成了字母 ‘A’、’B’ 和数字 ‘1’、’2’。combinedGenerator 是一个组合生成器函数,经过 yield* 调用了 generator1generator2,以达到将两者逻辑组合在一起的意图。当咱们遍历 combinedGenerator 的回来值时,会顺次输出 ‘A’、’B’、’1’、’2’。

探究 ES6 生成器 ( Generator ) 的异步编程运用

经过生成器的嵌套和组合,咱们可以更好地安排和复用生成器函数的逻辑,完结更灵敏和可扩展的功能。这种技术可以在异步编程、数据处理和状况办理等方面发挥重要作用,协助咱们更高效地处理杂乱的业务需求。

四. 总结

在本篇文章中,咱们了解了生成器中的过错处理,怎么运用生成器来完结可撤销的异步操作和并发操控,以及运用嵌套和组合这些技术来应对杂乱的异步场景。

经过生成器函数,咱们可以运用 yield 句子暂停和康复异步操作的履行,完结可撤销的异步操作。以注册撤销操作的回调函数,并在需求撤销操作时调用该回调函数,以中止异步操作的履行。这样,咱们就可以愈加灵敏地办理和操控异步操作。

此外,经过将生成器与 Promise 结合,咱们可以更方便地安排和办理异步操作的流程。这样,咱们可以以更清晰、易读的办法编写异步代码。

在处理杂乱的异步场景时,经过嵌套和组合生成器函数,可以完结更杂乱的异步操作流程,以满意特定的需求。

总的来说,ES6 生成器异步编程运用为咱们提供了更多的工具和技术,协助咱们处理杂乱的异步操作和过错处理。无论是撤销异步操作、完结并发操控,仍是嵌套和组合这些技术,咱们都可以依据详细的场景和需求,选择适宜的办法和技术,进步代码的可读性、可维护性和功能。经过不断探究这些新的思路,咱们可以更好地、更有技巧的应对异步编程带来的应战。