LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

JavaScript 异步:Generator与async/await

freeflydom
2024年8月7日 10:32 本文热度 964

Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同

前面的文章里我们介绍了回调函数promise 这两种手段来解决异步,本文将继续介绍异步发展史上的另外两种方法:Generatorasync/await


Generater

执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)

关键特性:

  1. 声明方式: Generator函数使用function*语法声明,星号(*)紧跟着function关键字后面。

  2. Yield表达式yield表达式用于暂停和恢复函数的执行。每次函数在yield处暂停时,它会返回yield后面的值给调用者。当再次调用生成器的next()方法时,函数会从上一个yield表达式之后的位置继续执行,直到下一个yield或函数结束。

  3. 返回遍历器对象: 执行Generator函数时,它不立即执行函数体内的代码,而是返回一个遍历器对象(iterator object)。这个对象具有next()方法,每次调用next()方法都会执行函数直到下一个yield表达式,或者直到函数结束。

  4. 遍历器协议: 遍历器对象遵循遍历器协议,next()方法返回一个对象,该对象有valuedone两个属性。valueyield表达式的结果,done是一个布尔值,表示是否到达了生成器函数的末尾。

函数定义:

Generator函数的定义与普通函数类似,但是有以下几点不同:

  • 函数声明前面需要加上星号*,表明这是一个Generator函数。

  • 函数体内可以使用yield表达式来“产出”一系列的值。yield表达式的作用类似于return,但不会结束函数,而是暂时挂起函数的执行,并在下次调用时从暂停的地方继续执行。

使用方法:

  1. 创建Generator对象:调用Generator函数时,它不会立即执行函数体内的代码,而是返回一个Generator对象。这个对象实现了迭代器协议,拥有next()方法。

因为此对象返回Iterator对象,所以我们可以通过for...of进行遍历

function* hai(){

  yield '嗨!';

  yield '你好吗?';

  yield '我很好!';

}

for (let h of hai()) {

  console.log(h);

}

  1. next()方法:调用Generator对象的next()方法会执行Generator函数直到遇到下一个yield表达式,或者直到函数结束。next()方法返回一个对象,该对象有两个属性:

    • valueyield表达式产生的值,如果没有更多的yield表达式,则为undefinedyield本身没有返回值,所以返回undefined)。

    • done:一个布尔值,表示是否到达了Generator函数的末尾。

运行逻辑:

  • 遇到yield表达式,就暂停执行后面的操作,并紧紧跟在yield后面的那个表达式的值,作为返回对象的value属性值。

  • 下次调用next方法时,再继续往下执行,直到遇到下一个yield表达式

  • 如果没有再遇到新的yield方法,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回对象的value

  • 如果该函数没有return语句,则返回的对象value属性值为undefind

function* hai(){

  yield '嗨!';

  yield '你好吗?';

  yield '我很好!';

}


var h = hai();


h.next()

h.next()

h.next()

h.next()

  1. 发送值给Generator函数next()方法还可以接受一个参数,这个参数将作为上一个yield表达式的值,从而可以向Generator函数内部发送数据。

function* foo(x) {

  var y = 2 * (yield (x + 1));

  var z = yield (y / 3);

  return (x + y + z);

}


var a = foo(5);

a.next()//{value: 6, done: false}    

a.next()//{value: NaN, done: false}

a.next()//value: NaN, done: true}


var b = foo(5);

b.next()//{value: 6, done: false}

b.next(12)//{value: 8, done: false}

b.next(13)//{value: 42, done: true}

a.next()

  • 第一次

  1. foo 函数开始执行,遇到第一个 yield 表达式 (x + 1),计算 x + 1 的值,即 5 + 1 = 6

  2. 6 被作为 a.next() 的 value 属性返回,done 属性为 false 表示生成器还没有执行完毕。

  • 第二次

  1. foo 函数继续执行,遇到 var y = 2 * (yield (x + 1));,由于上一个 yield 表达式的值被忽略了(没有通过 a.next(value) 提供),因此 y 的值是 2 * NaN = NaN

  2. 接下来遇到 var z =  yield (y / 3);,返回 y / 3 的值,即 NaN / 3 = NaN

  3. NaN 被作为 a.next() 的 value 属性返回,done 属性为 false 表示生成器还没有执行完毕

  • 第三次

  1. foo 函数继续执行,遇到 return (x + y + z);,计算 x + y + z 的值,即 5 + NaN + NaN = NaN

  2. NaN 被作为 a.next() 的 value 属性返回,done 属性为 true 表示生成器已经执行完毕。

b.next()

  • 第一次

  1. foo执行,遇到第一个 yield 表达式 (x + 1),计算 x + 1 的值,即 5 + 1 = 6

  2. 6 被作为 b.next() 的 value 属性返回,done 属性为 false 表示生成器还没有执行完毕。

  • 第二次

  1. 遇到 var y = 2 * (yield (x + 1));,由于上一个 yield 表达式的值被设置为 12,因此 y 的值是 2 * 12 = 24

  2. 接下来遇到 var z = y / 3;,计算 y / 3 的值,即 24 / 3 = 8

  3. 8 被作为 b.next() 的 value 属性返回,done 属性为 false 表示生成器还没有执行完毕。

  • 第三次

  1. 这一行代码执行后,foo 函数继续执行,遇到 yield (z);,由于上一个 yield 表达式的值被设置为 13,但这里 13 不会被使用。

  2. 接下来遇到 return (x + y + z);,计算 x + y + z 的值,即 5 + 24 + 8 = 37

  3. 37 被作为 b.next() 的 value 属性返回,done 属性为 true 表示生成器已经执行完毕。

示例

代码高亮:

function* countUpTo(max) {

  for (let i = 1; i <= max; i++) {

    yield i;

  }

}

const counter = countUpTo(3);

// 第一次调用next(),从1开始

console.log(counter.next()); // { value: 1, done: false }

// 第二次调用next(),输出2

console.log(counter.next()); // { value: 2, done: false }

// 最后一次调用next(),输出3

console.log(counter.next()); // { value: 3, done: false }

// 再次调用next(),Generator函数已经结束

console.log(counter.next()); // { value: undefined, done: true }

async/await

  • async/await是基于Promise的一种更高级的异步编程语法糖。

  • async函数总是返回一个Promise。

  • await关键字只能在async函数内部使用,用于等待Promise解析。

  • 使用await关键字可以让异步代码看起来和同步代码非常相似,极大地提高了可读性和可维护性。

  • async/await是目前处理异步操作最推荐的方式,因为它简洁且易于理解。

使用方法

async/await形式简洁,直接在函数前加上async然后在需要异步的操作前加上await即可

下面是使用 async/await 的基本步骤和示例:

步骤

  1. 定义异步函数:

    • 使用 async 关键字定义函数。

    • 这个函数自动返回一个 Promise

  2. 使用 await 关键字:

    • 在 async 函数内部,你可以使用 await 关键字等待一个 Promise 完成。

    • await 只能在 async 函数内部使用。

    • 当 await 一个 Promise 时,函数会暂停执行,直到 Promise 解析(resolve)或拒绝(reject)。

    • 如果 Promise 成功解析,await 表达式的值就是 resolve 的值;如果 Promise 被拒绝,则会抛出一个错误。

示例

假设我们有一个异步函数 getData 用于从服务器获取数据,然后我们使用 async/await 来处理这个异步操作。

Step 1: 定义数据获取函数

function getData() {

  return new Promise((resolve) => {

    setTimeout(() => {

      resolve('Hello from server!');

    }, 2000); // 模拟网络延迟

  });

}

Step 2: 使用 async/await 获取数据

async function a() {

  console.log(1);

  const data = await getData(); // 等待数据获取完成

  console.log(2);

  console.log(data);

}


// 调用异步函数

a();

解释

  1. 定义异步函数:

    • 使用 async 关键字定义了一个函数 a

    • 这个函数自动返回一个 Promise

  2. 使用 await 关键字:

    • 在 async 函数内部,我们使用 await 关键字等待 getData() 函数返回的 Promise 完成。

    • await 只能在 async 函数内部使用。

    • 当 await 一个 Promise 时,函数会暂停执行,直到 Promise 解析(resolve)或拒绝(reject)。

    • 如果 Promise 成功解析,await 表达式的值就是 resolve 的值;如果 Promise 被拒绝,则会抛出一个错误。

  3. 调用异步函数:

    • 最后,我们可以通过简单地调用 a() 来启动整个流程。

    • 由于 a 返回一个 Promise,可以通过 .then() 或 .catch() 方法来处理它的结果或错误。

运行示例

当运行这个示例时,会看到以下输出:

Hello from server!

这是因为:

  • 函数 a() 首先打印数字 1

  • 然后等待 getData() 返回的数据,该数据将在 2 秒后可用。

  • 当数据准备好时,a() 函数继续执行,打印数字 2 和获取到的数据 'Hello from server!'

异步解决方案的区别

  • promise和async/await是专门用于处理异步操作的

  • Generator 并不是为异步而设计出来的,它还有其他功能(对象迭代、控制输出、部署Interator接口等)

  • promise编码相比Generator、async更为复杂化,且可读性也稍差

  • Generator、async需要与promise对象搭配处理异步情况

  • async实质上是Generator的语法糖,相当于会自动执行的Generator函数

  • async使用上更为简洁,将异步代码以同步形式进行编写,是处理异步编程的最终方案


以上就是本文全部内容,希望对你有所帮助,感谢你的阅读!

来源:https://juejin.cn/post/7399986436349231144 作者:Alo365


该文章在 2024/8/7 10:32:12 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2024 ClickSun All Rights Reserved