首页 > 其他分享 >你是怎么理解ES6中 Generator的?使用场景?

你是怎么理解ES6中 Generator的?使用场景?

时间:2024-03-14 18:12:01浏览次数:30  
标签:function ES6 场景 函数 Generator yield next value

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

一、介绍

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

回顾下上文提到的解决异步的手段:

  • 回调函数
  • promise

那么,上文我们提到promsie已经是一种比较流行的解决异步方案,那么为什么还出现Generator?甚至async/await呢?

该问题我们留在后面再进行分析,下面先认识下Generator

Generator函数

执行 Generator 函数会返回一个遍历器对象,可以依次遍历 Generator 函数内部的每一个状态

形式上,Generator函数是一个普通函数,但是有两个特征:

  • function关键字与函数名之间有一个星号
  • 函数体内部使用yield表达式,定义不同的内部状态
function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

二、使用

Generator 函数会返回一个遍历器对象,即具有Symbol.iterator属性,并且返回给自己

function* gen(){
  // some code
}

var g = gen();

g[Symbol.iterator]() === g
// true

通过yield关键字可以暂停generator函数返回的遍历器对象的状态

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
var hw = helloWorldGenerator();

上述存在三个状态:helloworldreturn

通过next方法才会遍历到下一个内部状态,其运行逻辑如下:

  • 遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
  • 下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式
  • 如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
  • 如果该函数没有return语句,则返回的对象的value属性值为undefined
hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }

done用来判断是否存在下个状态,value对应状态值

yield表达式本身没有返回值,或者说总是返回undefined

通过调用next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值

function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{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 }

正因为Generator函数返回Iterator对象,因此我们还可以通过for...of进行遍历

function* foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  return 6;
}

for (let v of foo()) {
  console.log(v);
}
// 1 2 3 4 5

原生对象没有遍历接口,通过Generator函数为它加上这个接口,就能使用for...of进行遍历了

function* objectEntries(obj) {
  let propKeys = Reflect.ownKeys(obj);

  for (let propKey of propKeys) {
    yield [propKey, obj[propKey]];
  }
}

let jane = { first: 'Jane', last: 'Doe' };

for (let [key, value] of objectEntries(jane)) {
  console.log(`${key}: ${value}`);
}
// first: Jane
// last: Doe

三、异步解决方案

回顾之前展开异步解决的方案:

  • 回调函数
  • Promise 对象
  • generator 函数
  • async/await

这里通过文件读取案例,将几种解决异步的方案进行一个比较:

回调函数

所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,再调用这个函数

fs.readFile('/etc/fstab', function (err, data) {
  if (err) throw err;
  console.log(data);
  fs.readFile('/etc/shells', function (err, data) {
    if (err) throw err;
    console.log(data);
  });
});

readFile函数的第三个参数,就是回调函数,等到操作系统返回了/etc/passwd这个文件以后,回调函数才会执行

Promise

Promise就是为了解决回调地狱而产生的,将回调函数的嵌套,改成链式调用

const fs = require('fs');

const readFile = function (fileName) {
  return new Promise(function (resolve, reject) {
    fs.readFile(fileName, function(error, data) {
      if (error) return reject(error);
      resolve(data);
    });
  });
};


readFile('/etc/fstab').then(data =>{
    console.log(data)
    return readFile('/etc/shells')
}).then(data => {
    console.log(data)
})

这种链式操作形式,使异步任务的两段执行更清楚了,但是也存在了很明显的问题,代码变得冗杂了,语义化并不强

generator

yield表达式可以暂停函数执行,next方法用于恢复函数执行,这使得Generator函数非常适合将异步任务同步化

const gen = function* () {
  const f1 = yield readFile('/etc/fstab');
  const f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

async/await

将上面Generator函数改成async/await形式,更为简洁,语义化更强了

const asyncReadFile = async function () {
  const f1 = await readFile('/etc/fstab');
  const f2 = await readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

区别:

通过上述代码进行分析,将promiseGeneratorasync/await进行比较:

  • promiseasync/await是专门用于处理异步操作的

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

  • promise编写代码相比Generatorasync更为复杂化,且可读性也稍差

  • Generatorasync需要与promise对象搭配处理异步情况

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

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

四、使用场景

Generator是异步解决的一种方案,最大特点则是将异步操作同步化表达出来

function* loadUI() {
  showLoadingScreen();
  yield loadUIDataAsynchronously();
  hideLoadingScreen();
}
var loader = loadUI();
// 加载UI
loader.next()

// 卸载UI
loader.next()

包括redux-saga中间件也充分利用了Generator特性

import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'
import Api from '...'

function* fetchUser(action) {
   try {
      const user = yield call(Api.fetchUser, action.payload.userId);
      yield put({type: "USER_FETCH_SUCCEEDED", user: user});
   } catch (e) {
      yield put({type: "USER_FETCH_FAILED", message: e.message});
   }
}

function* mySaga() {
  yield takeEvery("USER_FETCH_REQUESTED", fetchUser);
}

function* mySaga() {
  yield takeLatest("USER_FETCH_REQUESTED", fetchUser);
}

export default mySaga;

还能利用Generator函数,在对象上实现Iterator接口

function* iterEntries(obj) {
  let keys = Object.keys(obj);
  for (let i=0; i < keys.length; i++) {
    let key = keys[i];
    yield [key, obj[key]];
  }
}

let myObj = { foo: 3, bar: 7 };

for (let [key, value] of iterEntries(myObj)) {
  console.log(key, value);
}

// foo 3
// bar 7

参考文献

  • https://es6.ruanyifeng.com/#docs/generator-async

标签:function,ES6,场景,函数,Generator,yield,next,value
From: https://www.cnblogs.com/smileZAZ/p/18073639

相关文章

  • C++、Java 和 Go 是三种流行的编程语言,它们各有不同的特点和应用场景
    C++:面向对象和泛型编程:C++是一种多范式编程语言,支持面向对象编程(OOP)和泛型编程(GenericProgramming)。性能:C++是一种系统级编程语言,注重性能和内存管理。它提供了直接的内存访问和指针操作,使得程序员可以更精细地控制内存和计算资源。灵活性:C++允许程序员直接操作硬件,并提......
  • 云审计与大数据审计:区别、优势与应用场景
    本文分享自天翼云开发者社区《云审计与大数据审计:区别、优势与应用场景》,作者:每日知识小分享随着信息技术的飞速发展,审计领域也迎来了革命性的变革。云审计和大数据审计作为两种新兴的审计模式,各有其独特的特点和应用场景。本文将对云审计和大数据审计进行深入的比较分析,探讨它们......
  • ArrayList和LinkedList底层原理的区别和使用场景
    (1)ArrayList底层是动态数组,查询快、增删慢。存储空间是连续的,可以根据寻址方式直接找到对应的元素位置,所以查询时间复杂度是o(1)。扩容:ArrayList支持动态扩容,在每次新增元素的时候会判断容量是否溢出,如果溢出则会进行扩容操作。当size=elementData.length时,表示数据数量已经超过......
  • volatile关键字用处和场景?
    什么是volatile关键字在C语言和Java中,它是一种特殊的类型修饰符,用来告诉编译器该变量可能会被意外地改变。这样,每次存取该变量时,编译器都不会对其进行缓存优化,而是直接从内存中读取或写入,提供了对特殊地址的稳定访问。在Java中,volatile提供了一种比synchronized更轻量级的同步......
  • 大宗物资快进快出场景下收款信息的及时获取
    前言    大宗物资行业实现货物的快速周转,对公司利润率影响很大,大宗物资特性决定了资金的大额收入,往往单次资金支付都是过百万,所以必须是通过银行支付,没办法通过微信或者支付宝。现有的一些银企直联系统,实际17点之后的收付流水就会在第二日做推送,但往往业务对资金......
  • [Javascript] Generator & Iterators exercise
    Generatorcanrunwithfor..ofand...,whichwillonlyemityieldvalues Forexample:function*count(){yield1;yield2;return3;}for(constvalueofcount()){console.log(value)//1,2}console.log([...count()])//[1,2] ......
  • 英码嵌入式AI ISP视频处理模组SOM928,使能全场景更智能、更清晰!
    随着视觉产业的飞速发展,图像分辨率获得持续提升,图像效果不断优化,但在暗光、逆光、运动等场景下输出高质量图像仍然是行业公认的难点。因此,能满足高清视觉感知应用的产品,在无人机、智能摄像机、高端运动相机、智能医疗视觉、智能驾驶等行业有着极大市场需求,英码嵌入式在视频图像技......
  • C# Onnx C2PNet 图像去雾 室外场景
    目录介绍效果模型信息项目代码下载C#OnnxC2PNet图像去雾室外场景介绍github地址:https://github.com/YuZheng9/C2PNet[CVPR2023]CurricularContrastiveRegularizationforPhysics-awareSingleImageDehazing效果模型信息ModelProperties-----------......
  • blender 改变场景,视图背景色
    打开blender软件,点击右上方【视图着色方式】展开图标。2在视图着色方式页面中,点击【背景/后台】选项的【视图】项。3点击【视图】选项下的颜色框,在弹出的颜色轮盘中,选择所要更改的颜色,即可。......
  • Nginx应用场景及配置
    目录一、Web服务器1.HTTP2.HTTPS二、反向代理三、负载均衡四、重定向1.老域名跳转新域名2.路径重定向五、防盗链六、手机端重定向PC端七、基于请求路径转发不同服务一、Web服务器1.HTTPserver{listen80;server_name_;location/{root/data;......