Javascript ES6中 Generator的?async/await Promise 了解一下?

介绍

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

生成器对象是由一个 generator function 返回的,并且它符合可迭代协议和迭代器协议。

generator和函数不同的是,generator由function*定义(注意多出的*号),并且,除了return语句,还可以用yield返回多次。

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

  • 回调函数
  • promise

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

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

Generator函数

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

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

  • function关键字与函数名之间有一个星号
  • 函数体内部使用yield表达式,定义不同的内部状态

function* helloWorldGenerator() { yield ‘hello’; yield ‘world’; return ‘ending’;}

函数只能返回一次,但是,如果换成generator,就可以一次返回一个数,不断返回多次。

什么是生成器?

生成器是在函数内部运行的一些代码

  • 返回值后,它会自行暂停,并且——
  • 调用程序可以要求取消暂停并返回另一个值

这种“返回”不是传统的从函数 return。所以它被赋予了一个特殊的名称——yield。

生成器语法因语言而异。Javascript 的生成器语法类似于 PHP,但是区别也很大,如果你希望它们的作用相同,那么最终你会感到非常困惑。

在 javascript 中,如果想要使用生成器,则需要:

  • 定义特殊的生成器函数
  • 调用该函数创建一个生成器对象
  • 在循环中使用该生成器对象,或直接调用其 next 方法

方法

Generator.prototype.next()返回一个由 yield表达式生成的值。

Generator.prototype.return()返回给定的值并结束生成器。

Generator.prototype.throw()向生成器抛出一个错误。

使用

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();

上述存在三个状态:hello、world、return

通过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这个文件以后,回调函数才会执行

传统的生成器对象

Firefox (SpiderMonkey) 在 JavaScript 1.7 中也实现了一个较早版本的生成器,其中函数声明中的星号(*)不是必需的 (只需在函数体中使用yield 关键字)。但是,旧式生成器已弃用。不要使用它们;他们将被删除 (bug 1083482)。

传统的生成器方法

Generator.prototype.next() Non-Standard返回 yield 表达式产生的值。与 ES2015 生成器对象的 next() 方法对应。

Generator.prototype.close() Non-Standard关闭生成器,因此执行该函数后调用next() 函数时将会抛出 StopIteration 错误。与 ES2015 生成器对象的 return() 方法对应..

Generator.prototype.send() Non-Standard用于将值发送到生成器。 该值由 yield 表达式返回,并且返回下一个 yield 表达式产生的值。send(x) 对应于 ES2015 生成器对象中的 next(x)

Generator.prototype.throw() Non-Standard向生成器抛出错误。与 ES2015 生成器对象的 throw() 方法对应。

generator函数中的this问题

// 正常函数function F () { this.a = 1}const f = new F()// 调用构造函数F,返回实例对象f// 将构造函数内部中的this指向这个实例对象// 将构造函数中的原型对象赋值给实例对象的原型// 执行构造函数中的代码// 调用Generator函数会返回遍历器对象,而不是实例对象,因此无法获取到this指向的实例对象上的私有属性和方法。但是这个遍历器对象可以继承Generator函数的prototype原型对象上的属性和方法(公有属性和方法)。function* g() {}g.prototype.hello = function () { return ‘hi!’;};let obj = g();obj instanceof g // trueobj.hello() // ‘hi!’

上面代码表明,Generator 函数g返回的遍历器obj,是g的实例,而且继承了g.prototype。但是,如果把g当作普通的构造函数,并不会生效,因为g返回的总是遍历器对象,而不是this对象。

应用generator 数组扁平化

function* flatten3(arr) { let length = arr.length; for (let i=0; i<length; i++) { let item = arr[i]; if (Array.isArray(item)) { yield* flatten3(item); } else { yield item; } }} let res = [];for (let f of flatten3 (arr)) { res.push(f);}console.log(res)

迭代方法

function flatten2(arr) { const stack = […arr]; const res = []; while (stack.length) { // 从栈里取出 const next = stack.pop(); if (Array.isArray(next)) { // 把next扁平化,然后放入stack中 stack.push(…next); } else { res.push(next); } } // reverse to restore input order return res.reverse();}console.log(flatten2(arr))

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());};

区别:

通过上述代码进行分析,将promise、Generator、async/await进行比较:

  • promise和async/await是专门用于处理异步操作的
  • Generator并不是为异步而设计出来的,它还有其他功能(对象迭代、控制输出、部署Interator接口…)
  • promise编写代码相比Generator、async更为复杂化,且可读性也稍差
  • Generator、async需要与promise对象搭配处理异步情况
  • async实质是Generator的语法糖,相当于会自动执行Generator函数
  • async使用上更为简洁,将异步代码以同步的形式进行编写,是处理异步编程的最终方案

使用场景

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

function* loadUI() { showLoadingScreen(); yield loadUIDataAsynchronously(); hideLoadingScreen();}var loader = loadUI();// 加载UIloader.next()// 卸载UIloader.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

给大家分享我收集整理的各种学习资料,前端小白交流、学习交流,也可以直接问我,我会组织大家一起做项目练习,帮助大家匹配一位学习伙伴互相监督学习-下面是学习资料参考。

前端学习交流、自学、学习资料等推荐 – 知乎

郑重声明:本文内容及图片均整理自互联网,不代表本站立场,版权归原作者所有,如有侵权请联系管理员(admin#wlmqw.com)删除。
(0)
用户投稿
上一篇 2022年6月27日
下一篇 2022年6月27日

相关推荐

  • 远离油头,头屑,原来如此简单

    远离油头,头屑,原来如此简单 选对方法,找准方向 原来去屑,去油如此简单

    2022年6月10日
  • 板栗怎么去皮和壳小妙招【挑选板栗的小技巧】

    俗话说“入秋食板栗,健胃又补脾”,而每年的9月份,正是板栗大量上市的季节,而我感觉今年的板栗不仅比去年便宜,而且还更甜一些,所以在我们当地,差不多每个人最近都会买2-3斤的板栗给家…

    2022年5月12日
  • 有什么专业的难度是被严重低估的?

    我觉得是数学。 对学霸学神来说,数学是很简单的,至少在被打击之前是这么认为的。 我身边有两个例子: A同学:从小就是“别人家的孩子”,数学竞赛保送北大,自信满满地选了数学系,大二时…

    2022年8月19日
  • 杨绛先生:人生实苦

    杨绛先生说:“在这个物欲横流的人世间,人生一世实在是够苦的。你存心做一个与世无争的老实人吧,人家就利用你,欺侮你。你稍有才德品貌,人家就嫉妒你、排挤你。你大度退让,人家就侵犯你、损…

    2022年6月29日
  • 有知道什么好的瘦身方法吗?

    健身还是有小诀窍的,而且健身还需要正确的方法,才能更有利于更好的减脂,接下来,我们从几个方面来考虑。 1、饮食,减肥时不要盲目的戒掉东西,更不要什么都不要吃,平时如果减肥的话,可以…

    2022年8月26日
  • 显示好友对自己发布内容 点赞 喜欢 评论信息 逻辑 参考代码

    controller /** * 查询消息点赞列表 * * @param page * @param pageSize * @return */ @GetMapping(&#822…

    2022年6月17日
  • 600万美元皮肤失窃,CS:GO最大交易平台遭黑客攻击

    《CS:GO》(反恐精英:全球攻势)是近来最热门的多人第一人称射击游戏之一,由知名电子游戏开发商Valve Software发行。该游戏中的武器皮肤具有不同的稀有性,这就促使了使用…

    2022年8月18日
  • 真的有两个宇宙?镜像宇宙理论,或能解决宇宙膨胀之谜

    大约138亿年前,我们的宇宙还是一个奇点,今天宇宙中所有的物质都集中在这个没有空间的奇点内,甚至连时间都不存在。 突然,这个奇点发生了大爆炸,诞生了今天的宇宙,也创造了时间和空间,…

    2022年6月13日
  • 手和脚怎么可以变白 最有效的美白方法与技巧 爱美姐妹赶紧拿去

    比较爱美的姐妹,常常会给自己的双手双脚做美甲,也就是涂上鲜艳的颜色,粘上漂亮的饰品,让自己即使是在人群中,也能够更加耀眼夺目。若是手脚黑的话效果就截然相反了,所以对于手部足部的美白…

    2022年8月12日
  • 杨幂高清时尚穿搭美图

    #美图壁纸# #女明星高清美图# #明星高清生图# #每日搭配分享时尚穿搭 不管是漂亮衣服,还是心动对象,时髦女孩都喜欢先下手为强# #女明星妩媚穿搭#

    2022年7月11日

联系我们

联系邮箱:admin#wlmqw.com
工作时间:周一至周五,10:30-18:30,节假日休息