电脑网站搜索如何做,wordpress腾讯云cdn,如何做采集网站,wordpress高亮代码添加行号Generator函数
基本概念
Generator函数时ES6提供的一种异步编程解决方案#xff1b;
一、语法上#xff1a;
可以理解为一个状态机#xff0c;封装了多个内部状态执行Generator函数会返回一个遍历器对象
二、形式上的特点#xff1a;
function命令和函数名之间有一个…Generator函数
基本概念
Generator函数时ES6提供的一种异步编程解决方案
一、语法上
可以理解为一个状态机封装了多个内部状态执行Generator函数会返回一个遍历器对象
二、形式上的特点
function命令和函数名之间有一个星号*星号位置没有明确规定我们一般紧跟function后函数内部使用yield定义不同的状态 function* genetatorTest() {yield start;yield running;return finished;}三、调用与执行-分段
调用也是使用小括号但是调用Generator函数后该函数并不执行返回的也不是函数运行的结果而是一个遍历器对象 Iterator Object
Generator函数分段执行yield语句是暂停执行的标记而next 方法可以恢复执行
Generator函数返回的遍历器对象每次调用 next() 方法内部指针就从函数头部或上一次停下来的方法开始执行直到遇到下一条 yield 或 return 语句为止
next方法返回一个对象 { value: 状态值, done: 遍历是否完成boolean} function* genetatorTest() {console.log(1);yield start;console.log(2);yield running;return finished;}const obj genetatorTest();console.log(obj);const re1 obj.next(); // {value: start, done: false}console.log(re1);const re2 obj.next(); // {value: running, done: false}console.log(re2);const re3 obj.next(); // {value: finished, done: true}console.log(re3);console.log(obj.next()); // {value: undefined, done: true}调用Generator函数的时候函数并没有执行只是返回了一个遍历器对象因此并没有打印1
第一次调用next(),函数开始执行打印出1,直到遇到yield停下 返回一个对象包含value当前内部状态值yield 或 return 后面的值和done布尔值遍历是否结束属性第二次调用next(), 函数接着执行遇到yield停下第三次调用next(), 函数接着执行遇到return,函数执行完成如果没有return语句就执行到函数结束第四次调用函数已经执行完成了返回对象属性为undefined,done为true,以后调用都是这个值
yield表达式
一、暂停的标志
只有调用了next 方法才会执行 yield 语句后面的表达式因此等于为 JS 提供了惰性求值的功能
二、yield和return的比较
相似点都可以返回紧跟后面的表达式的值
区别每次遇到yield就会暂停执行下一次会从该位置继续往下执行而return没有位置记忆的功能遇到return整个函数终止执行没有多个返回值页不能暂停
三、Generator函数可以不使用yield,此时就是一个单纯的暂缓执行函数 function* generator() {console.log(命运的此轮停止旋转);}const iObj generator();iObj.next();四、yield表达式只能用在 Generator 函数里面用在其他地方会报错 function fn() {console.log(这样会报错);yield test // Uncaught SyntaxError: Unexpected string}fn()五、yield表达式如果用在另一个表达式中必须放在小括号中 function* demo(x) {console.log(1 (yield));console.log(2 (yield 1 x));}六、yield表达式用作函数的参数或放在复制表达式的右边可以不加小括号 function* demo(x) {const yy yield 11;generator(yield x);}next()方法
next方法的运行逻辑
遇到yield语句就暂停执行后面的操作并将紧跟在yield后的表达式的值作为返回对象 value 的属性值下一次调用 next 方法继续往下执行直到遇到下一条 yield 语句如果没有遇到新的 yield 语句就一直运行到函数结束直到遇到 return 语句为止并将 return 语句后的表达式的值作为返回对象 value 的属性值如果函数没有 return语 句则返回对象 value 的属性值为 undefined
next方法的参数
yield语句本身没有返回值或者说总是返回undefined,而next可以带一个参数这个参数会被当作上一条yield的返回值
因此通过next方法的参数可以实现在Generator函数运行的不同阶段从外部注入不同值
next不传入参数 function* fn(x) {const y yield x 2;const z 2 * (yield x);return x y z;}const fnObj fn(2);fnObj.next();fnObj.next();const result fnObj.next(); // {value: NaN, done: true}分析第一次调用next启动遍历器对象遇到yield暂停返回一个对象{value:4,done:false}第二次调用next,由于yield返回值为undefined,所以 yundefined,遇到yield暂停返回 {value:2,done:false}第三次调用next,z为2与undefined相乘所以为NaN最后返回对象 {value:NaN,done:true}
next传入参数 function* fn(x) {const y yield x 2;const z 2 * (yield x);return x y z;}const fnObj fn(2);fnObj.next();fnObj.next(4);fnObj.next(2);console.log(result);分析第一次调用next启动遍历器对象遇到yield暂停返回一个对象{value:4,done:false}; 第二次调用next,传入的参数4作为上一条yield表达式的返回值,所以 y4,遇到yield暂停返回 {value:2,done:false}第三次调用next,传入参数2所以z2*24;最后返回对象 {value:10,done:true}
第一次使用next方法传参是无效的只是用来启动遍历器对象
与Iterator接口的关系
Symbol.iterator方法等于该对象的遍历器对象生成函数调用该函数会返回该对象的一个遍历器对象
由于Generator函数就是遍历器生成函数因此可以把Generator函数赋值给对象的Symbol.iterator属性从而使得该对象具有Iterator接口 const myIterable {};myIterable[Symbol.iterator] function* () {yield 1;yield 一;yield one;};console.log(...myIterable); // 1 一 oneGenerator函数执行后返回一个遍历器对象改对象本身也具有 [Synbol.iterator] 属性执行后返回自身 function* gen(params) {}const genObj gen();genObj[Symbol.iterator]() genObj; // truefor of循环
for…of 循环可以自动遍历 Generator 函数生成的 Iterator 对象并且此时不再需要调用next方法
一旦next方法返回的对象done属性为true for…of 就会终止循环并且不包含该返回对象所以下面代码return语句返回的值没有打印 function* genFn() {yield 1;yield 2;yield hello;yield everybody;return have a nice day;}for (let k of genFn()) {console.log(k); // 1 2 hello everybody}原生的 JavaScript 对象没有遍历接口无法使用 for… of 循环通过Generator函数为它加上这个接口就可以用了 function* objectEntries(obj) {let propKeys Reflect.ownKeys(obj);for (let key of propKeys) {yield [key, obj[key]];}}const tempObj { name: 如花, age: 28, gender: 未知 };const arr [];for (let k of objectEntries(tempObj)) { // for...of遍历遍历器对象arr.push(k);console.log(k);}console.log(arr);// console.log(Object.entries(tempObj));Generator.prototype.throw
Generator函数返回的遍历器对象都有一个throw方法可以在函数体外抛出错误然后在Generator函数体内捕获 function* genFunc(params) {try {yield;} catch (err) {console.log(函数内部捕获异常, err);}}const g genFunc();g.next();try {g.throw(错误信息msg1);g.throw(错误信息msg2);} catch (err) {console.log(函数外部捕获异常, err);}上面代码遍历器对象连续抛出两个异常第一个被Generator函数内部捕获第二个异常由于Generator函数内部的catch已经执行过了不会再捕获这个错误了所以这个错误抛出了Generator函数体被外部的catch捕获
注意这里都是使用遍历器对象的throw方法抛出异常而不是全局的throw(),如果上面是使用全局的throw抛出异常只有函数外的catch能捕获
如果于Generator函数内部没有 try… catch ,异常会被函数外的 catch 捕获 function* genFunc(params) {yield;}const g genFunc();g.next();try {g.throw(错误信息msg1);} catch (err) {console.log(函数外部捕获异常, err);}如果于Generator函数内部有 try… catch ,那么遍历器的throw方法抛出的错误不影响下一次遍历否则遍历直接终止 function* genFunc(params) {try {yield 1;yield 2;yield 3;} catch (err) {console.log(内部捕获, err);}}const g genFunc();g.next();console.log(g.throw()); // {value: undefined, done: true}console.log(g.next()); // {value: undefined, done: true}这里捕获到错误try中的代码是不会执行了的如果generator中错误之后还有代码会执行这里后面没有了所以执行了throw后整个函数就执行完了
抛出错误没捕获和throw一样程序终止执行了 function* genFunc(params) {yield 1;yield 2;}const g genFunc();g.next();g.throw(); // 报错:Uncaught undefinedg.throw被捕获后会执行catch中代码执行完后继续执行直到遇到yield暂停throw也会触发一次next的启动让函数往下走 function* genFunc(params) {try {yield 1;} catch (err) {console.log(内部捕获, err);}yield 2;yield 3;}const g genFunc();g.next();console.log(g.throw()); // {value: 2, done: false}console.log(g.next()); // {value: 3, done: false}Generator.prototype.return
返回给定的值并中介Generator函数的遍历
使用return方法状态直接变为done后面再调用next,就是 {value: undefined, done: true} function* genFunc(params) {yield 1;yield 2;yield 3;}const g genFunc();g.next();console.log(g.return(终止了)); // {value: 终止了, done: true}console.log(g.next()); // {value: undefined, done: true}当Generator函数内部有try…finally 调用return方法就开始执行finally代码finally中代码执行完再执行return方法 function* generatorFn() {yield 1;try {yield 2;yield 3;} finally {yield 4;yield 5;}}const iteratorObj generatorFn();const obj11 iteratorObj.next(); //{value: 1, done: false}const obj22 iteratorObj.next(); //{value: 2, done: false}const obj33 iteratorObj.return(66); // {value: 4, done: false}const obj44 iteratorObj.next(); // {value: 5, done: false}const obj55 iteratorObj.next(); // {value: 66, done: true}const obj66 iteratorObj.next(); // {value: undefined, done: true}yield*表达式
yield* 语句可以Generator函数执行另一个Generator函数
如果直接调用gen1()是没有效果的如果没有加*, 直接使用 yield 调用就是返回一个遍历器对象 function* gen1(params) {yield 1;yield 2;}function* gen2(params) {yield 3;yield* gen1();yield 4;}等同于 function* gen2(params) {yield 3;yield 1;yield 2;yield 4;}for (let k of gen2()) {console.log(k); // 3 1 2 4}Generator函数this
Generator 函数返回的总是遍历器对象不是this对象
问题
Generator 函数在this对象上面添加了一个属性但是该函数的实例对象拿不到这个属性Generator 函数不能跟new命令一起用会报错
解决首先使用call方法将 Generator 函数的this绑定成自身的protoytype再将 Generator 函数改成构造函数就可以对它执行new命令了 Generator 函数中this通过call指向了gen的原型 function* gen() {this.a 1; // this gen.prototypeyield (this.b 2);}function F() {return gen.call(gen.prototype);}var f new F();f.next(); // Object {value: 2, done: false}f.next(); // Object {value: undefined, done: true}console.log(f.a); // 1console.log(f.b); // 2Generator函数与状态机制
generator是实现状态机的最佳解构
调用一次函数就修改一次bool的状态值 var bool true;var changeStatus function () {console.log(bool);if (bool) {console.log(1);} else {console.log(2);}bool !bool;};等价与generator的写法 function* statusGen(params) {while (true) {console.log(1);yield;console.log(2);yield;}}这种写法不需要保存状态的外部遍历更加简洁安全状态不会被非法篡改
应用
异步操作的同步化表达 function* loadFn() {const dom document.querySelector(.box);dom.innerText 加载中;const re yield asyncTask();dom.innerText re;}function asyncTask() {setTimeout(() {loadingObj.next(result:请求到的数据);}, 3000);}const loadingObj loadFn();loadingObj.next();控制流管理 step1(function (value1) {step2(value1, function (value2) {step3(value2, function (value3) {});});});promise改写 Promise.resolve(step1).then(step2).then(step3).catch((e) {});generator写法 function* runTask(params) {try {yield step1(value1);yield step1(value1);yield step1(value1);} catch (e) {}}部署Iterator接口
利用Generator函数可以在任意对象上部署Iterator接口
Generator函数的异步应用
异步
异步可以理解为一个任务不是连续完成的任务完成了一部分之后过了会才执行剩余部分
ES6之前异步编程的方案大概有以下几种方案回调函数、事件监听、订阅发布、promise
回调函数 function fn2(err) {if (err) throw err;console.log(执行第二部分);}function fn(callback fn2) {try {console.log(ff);console.log(执行第一部分);callback();} catch (err) {callback(err);}}fn();将任务分段处理通过调用回调函数执行其他部分
问题多个函数的嵌套多层函数嵌套形成回调地狱不好阅读、维护
promise
promise就是为了解决回调地狱而被提出来的
它将回调函数的嵌套改成了链式调用.then和.catch
问题代码冗余多个then的堆积
generator与协程
协程是程序运行的一种方式是一种多任务的解决方案可以理解为 “协作的线程” 或 “协作的函数”
子例程与协程的区别
子例程采用堆栈式先进后出的执行方式只有当调用的子函数完全执行完毕才会结束执行夫函数多个线程或函数可以并行执行但是只有一个处于正在运行状态其它处于暂停状态线程或函数之间可以交换执行权
在内存中子例程只是用一个栈而协程使用多个栈但是只有一个栈处于正在运行状态协程是以多占用内存为代价实现多任务的并行运行
由于JS是单线程语言只能保持一个调用栈。引入协程后每个任务可以保持自己的调用栈。好处就是抛出错误的时候可以找到原始的调用栈
协程有点像函数又有点像线程它的大概执行流程
协程A开始执行协程A执行到一半进入暂停状态执行权转移到携程B中一段时间后协程B交换执行权协程A恢复执行
Generator函数可以暂停执行和恢复执行这是它能封装异步任务的根本原因另外函数体内外的数据交互和错误处理机制也使得它能够更好处理成异步编程
generator对于异步任务的封装 const fetch require(node-fetch);function* gen() {const url https://api.github.com/users/github;const result yield fetch(url);return result;}const genObj gen();genObj.next().value.then((res) {return data.json();}).then((res) {genObj.next(res);});Thunk函数
Thunk函数是自动执行Generator函数的一种方法
Thunk函数介绍 const x 1;function fn(params) {return params 1;}fn(x 2);参数求值策略什么时候计算x2
1》传值调用
在进入函数体之前就计算参数的值
2》传名调用
直接将表达式传入函数体只在用到它的时候求值
Thunk函数是“传名调用”的一种实现方式就是将函数参数放到一个临时函数中再将这个临时函数传入函数体这个临时函数就是Thunk函数 const x 1;function fn(params) {return params 1;}fn(x 2);等同于 const thunk function () {return x 2;};function fn() {return thunk() 1;}JS中的Thunk函数
JS语言是传值调用它的Thunk函数含义有所不同
它是将多参函数替换成一个只接受回调函数作为参数的单参函数 // 正常的多参函数fs.readFile(fileName, callback);// thunk版本const Thunk function (fileName) {return function (callback) {fs.readFile(fileName, callback);};};const readThunk Thunk(fileName);readThunk(callback);流程管理 function* gen(params) {// ……}const g gen(0);const res g.next();while (!res.done) {g.next();}前面有看过这段代码Generator函数gen回自动执行完所有步骤
但是不适合异步操作这时Thunk函数就派上用处了
基于回调的自动执行
基于Promise的自动执行 function run(gen) {const genObj gen();function next(data) {const result genObj.next(data);if (result.done) return result.value;result.value.then((data) {next(data);});}next();}function* gen(params) {re1 yield asncPromise(11);console.log(1);re2 yield asncPromise(22);console.log(2);re3 yield asncPromise(33);console.log(3);re4 yield asncPromise(44);console.log(4);return re4;}function asncPromise(num) {return new Promise((resolve) {setTimeout(() {console.log(num);resolve(12);}, Math.random() * 1000);});}run(gen);async、await与generator
Generator函数: function asyncOperate(num) {return new Promise((resolve) {const time Math.random() * 2000;setTimeout(() {console.log(num, time);resolve(num * 2);}, time);});}function* gen() {const re1 yield asyncOperate(1);const re2 yield asyncOperate(2);console.log(re1, re2);}function run(gen) {const genObj gen();function next(data) {const result genObj.next(data);if (result.done) return result.value;result.value.then((data) {next(data);});}next();}run(gen);async await: async function fn() {const re1 await asyncOperate(1);const re2 await asyncOperate(2);console.log(re1, re2);}function asyncOperate(num) {return new Promise((resolve) {const time Math.random() * 2000;setTimeout(() {console.log(num, time);resolve(num * 2);}, time);});}fn();async函数实际上就是将Generator函数的星号 * 替换成 async ; 将 yield 替换成 await
async函数对Generator函数的改进
1》自带执行器而Generator函数的执行依靠执行器co模块就是用于Generator函数的自动执行
2》语义更加清晰async说明函数中有异步操作await是等待异步执行
3》返回值是promise,更加方便Generator返回的是Iterator对象
4》适用性更广co模块规定yield后面跟异步或promise保证执行顺序await后面可以是promise,也可以是原始类型值
await后面一般是跟一个Promise,如果不是会被转为一个resolved状态的Promise