This article brings you relevant knowledge about JavaScript. It mainly introduces you to the detailed explanation of the JS Generator function yield expression example. The Generator function is an asynchronous programming solution provided by ES6. , let’s take a look at it, I hope it will be helpful to everyone.
[Related recommendations: JavaScript video tutorial, web front-end]
In Javascript, once a function starts executing, it will run to the end or end when it encounters return. No other code can interrupt it during execution, and no value can be passed into the function body from the outside
The emergence of the Generator function (generator) makes it possible to break the complete operation of the function, and its grammatical behavior is completely different from traditional functions
The Generator function is an asynchronous programming solution provided by ES6 , it is also an ordinary function in form, but has several notable features:
// 传统函数 function foo() { return 'hello world' } foo() // 'hello world',一旦调用立即执行 // Generator函数 function* generator() { yield 'status one' // yield 表达式是暂停执行的标记 return 'hello world' } let iterator = generator() // 调用 Generator函数,函数并没有执行,返回的是一个Iterator对象 iterator.next() // {value: "status one", done: false},value 表示返回值,done 表示遍历还没有结束 iterator.next() // {value: "hello world", done: true},value 表示返回值,done 表示遍历结束
You can see the traditional function in the above code It is completely different from the operation of the Generator function. The traditional function is executed immediately after the call and the return value is output; the Generator function is not executed but returns an Iterator object and is traversed by calling the next method of the Iterator object. The execution in the function body can be seen It feels more like "being kicked before moving"
function* gen() { yield 'hello' yield 'world' return 'ending' } let it = gen() it.next() // {value: "hello", done: false} it.next() // {value: "world", done: false} it.next() // {value: "ending", done: true} it.next() // {value: undefined, done: true}
The above code defines a Generator function, which contains two yield expressions and a return statement (that is, three states are generated )
Every time the next method of the Iterator object is called, the internal pointer will start execution from the head of the function or the place where it stopped last time, until it encounters the next yield expression or return statement to pause. In other words, the Generator function is executed in sections, the yield expression is a mark for pausing execution, and the next method can resume execution.
When the next method is called for the fourth time, since the function has completed the traversal, it will no longer There are other states, so {value: undefined, done: true} is returned. If you continue to call the next method, this value will be returned
yield expression can only be used in the Generator function, and an error will be reported if used elsewhere
function (){ yield 1; })() // SyntaxError: Unexpected number // 在一个普通函数中使用yield表达式,结果产生一个句法错误
yield expression must be placed inside parentheses if used in another expression
function* demo() { console.log('Hello' + yield); // SyntaxError console.log('Hello' + yield 123); // SyntaxError console.log('Hello' + (yield)); // OK console.log('Hello' + (yield 123)); // OK }
yield expression is used as a parameter or placed on the right side of an assignment expression, and parentheses are not required
function* demo() { foo(yield 'a', yield 'b'); // OK let input = yield; // OK }
Similar: both can return the value of the expression immediately following the statement
Difference:
If you call another Generator function inside the Generator function, it will have no effect by default
function* foo() { yield 'aaa' yield 'bbb' } function* bar() { foo() yield 'ccc' yield 'ddd' } let iterator = bar() for(let value of iterator) { console.log(value) } // ccc // ddd
In the above example, when using for...of to traverse the traverser object generated by function bar, only the two status values of bar itself are returned. At this time, if you want to correctly call foo in bar, you need to use the yield* expression
yield* expression is used to execute another Generator function inside a Generator function
function* foo() { yield 'aaa' yield 'bbb' } function* bar() { yield* foo() // 在bar函数中 **执行** foo函数 yield 'ccc' yield 'ddd' } let iterator = bar() for(let value of iterator) { console.log(value) } // aaa // bbb // ccc // ddd
The yield expression itself has no return value, or it always returns undefined. The next method can take a parameter, which will be treated as the return value of the previous yield expression
function* gen(x) { let y = 2 * (yield (x + 1)) // 注意:yield 表达式如果用在另一个表达式中,必须放在圆括号里面 let z = yield (y / 3) return x + y + z } let it = gen(5) /*** 正确的结果在这里 ***/ console.log(it.next()) // 首次调用next,函数只会执行到 “yield(5+1)” 暂停,并返回 {value: 6, done: false} console.log(it.next()) // 第二次调用next,没有传递参数,所以 y的值是undefined,那么 y/3 当然是一个NaN,所以应该返回 {value: NaN, done: false} console.log(it.next()) // 同样的道理,z也是undefined,6 + undefined + undefined = NaN,返回 {value: NaN, done: true}
If a parameter is provided to the next method, the return result will be completely different
{ function* gen(x) { let y = 2 * (yield (x + 1)) // 注意:yield 表达式如果用在另一个表达式中,必须放在圆括号里面 let z = yield (y / 3) return x + y + z } let it = gen(5) console.log(it.next()) // 正常的运算应该是先执行圆括号内的计算,再去乘以2,由于圆括号内被 yield 返回 5 + 1 的结果并暂停,所以返回{value: 6, done: false} console.log(it.next(9)) // 上次是在圆括号内部暂停的,所以第二次调用 next方法应该从圆括号里面开始,就变成了 let y = 2 * (9),y被赋值为18,所以第二次返回的应该是 18/3的结果 {value: 6, done: false} console.log(it.next(2)) // 参数2被赋值给了 z,最终 x + y + z = 5 + 18 + 2 = 25,返回 {value: 25, done: true} }
ES6 stipulates that the default Iterator interface is deployed in the Symbol.iterator property of the data structure, or in other words, as long as a data structure has the Symbol.iterator property, it can be considered "traversable" (iterable).
Symbol.iterator property itself is a function, which is the default traverser generation function of the current data structure. Executing this function will return a traverser.
Since executing the Generator function actually returns an iterator, the Generator can be assigned to the Symbol.iterator property of the object, so that the object has the Iterator interface.
{ let obj = {} function* gen() { yield 4 yield 5 yield 6 } obj[Symbol.iterator] = gen for(let value of obj) { console.log(value) } // 4 // 5 // 6 console.log([...obj]) // [4, 5, 6] }
传统对象没有原生部署 Iterator接口,不能使用 for...of 和 扩展运算符,现在通过给对象添加Symbol.iterator
属性和对应的遍历器生成函数,就可以使用了
由于 Generator 函数运行时生成的是一个 Iterator 对象,因此,可以直接使用 for...of 循环遍历,且此时无需再调用 next() 方法
这里需要注意,一旦 next() 方法的返回对象的 done 属性为 true,for...of 循环就会终止,且不包含该返回对象
{ function* gen() { yield 1 yield 2 yield 3 yield 4 return 5 } for(let item of gen()) { console.log(item) } // 1 2 3 4 }
Generator 函数返回的遍历器对象,还有一个 return 方法,可以返回给定的值(若没有提供参数,则返回值的value属性为 undefined),并且 终结 遍历 Generator 函数
{ function* gen() { yield 1 yield 2 yield 3 } let it = gen() it.next() // {value: 1, done: false} it.return('ending') // {value: "ending", done: true} it.next() // {value: undefined, done: true} }
应用一:假定某公司的年会上有一个抽奖活动,总共6个人可以抽6次,每抽一次,抽奖机会就会递减
按照常规做法就需要声明一个全局的变量来保存剩余的可抽奖次数,而全局变量会造成全局污染,指不定什么时候就被重新赋值了,所以往往并不被大家推荐
{ let count = 6 // 声明一个全局变量 // 具体抽奖逻辑的方法 function draw() { // 执行一段抽奖逻辑 // ... // 执行完毕 console.log(`剩余${count}次`) } // 执行抽奖的方法 function startDrawing(){ if(count > 0) { count-- draw(count) } } let btn = document.createElement('button') btn.id = 'start' btn.textContent = '开始抽奖' document.body.appendChild(btn) document.getElementById('start').addEventListener('click', function(){ startDrawing() }, false) }[object Object]
事实上,抽奖通常是每个人自己来抽,每抽一次就调用一次抽奖方法,而不是点一次就一次性就全部运行完,是可暂停的,这个不就是 Generator 函数的意义所在吗?
// 具体抽奖逻辑的方法 function draw(count) { // 执行一段抽奖逻辑 // ... console.log(`剩余${count}次`) } // 执行抽奖的方法 function* remain(count) { while(count > 0) { count-- yield draw(count) } } let startDrawing = remain(6) let btn = document.createElement('button') btn.id = 'start' btn.textContent = '开始抽奖' document.body.appendChild(btn) document.getElementById('start').addEventListener('click', function(){ startDrawing.next() }, false)
应用二:由于HTTP是一种无状态协议,执行一次请求后服务器无法记住是从哪个客户端发起的请求,因此当需要实时把服务器数据更新到客户端时通常采用的方法是长轮询和Websocket。这里也可以用 Generator 函数来实现长轮询
{ // 请求的方法 function* ajax() { yield new Promise((resolve, reject) => { // 此处用一个定时器来模拟请求数据的耗时,并约定当返回的json中code为0表示有新数据更新 setTimeout(() => { resolve({code: 0}) }, 200) }) } // 长轮询的方法 function update() { let promise = ajax().next().value // 返回的对象的value属性是一个 Promise 实例对象 promise.then(res => { if(res.code != 0) { setTimeout(() => { console.log('2秒后继续查询.....') update() }, 2000) } else{ console.log(res) } }) } update() }
【相关推荐:JavaScript视频教程、web前端】
The above is the detailed content of Detailed explanation of generator function yield expression example in JavaScript. For more information, please follow other related articles on the PHP Chinese website!