知识点简单回顾
在Generator函数语法解析篇的文章中有说到,Generator函数可以定义多个内部状态,同时也是遍历器对象生成函数。yield表达式可以定义多个内部状态,同时还具有暂停函数执行的功能。调用Generator函数的时候,不会立即执行,而是返回遍历器对象。
遍历器对象的原型对象上具有next方法,可以通过next方法恢复函数的执行。每次调用next方法,都会在遇到yield表达式时停下来,再次调用的时候,会在停下的位置继续执行。调用next方法会返回具有value和done属性的对象,value属性表示当前的内部状态,可能的值有yield表达式后面的值、return语句后面的值和undefined;done属性表示遍历是否结束。
yield表达式默认是没有返回值的,或者说,返回值为undefined。因此,想要获得yield表达式的返回值,就需要给next方法传递参数。next方法的参数表示上一个yield表达式的返回值。因此在调用第一个next方法时可以不传递参数(即使传递参数也不会起作用),此时表示启动遍历器对象。所以next方法会比yield表达式的使用要多一次。
更加详细的语法可以参考这篇文章。
异步任务的封装
yield表达式可以暂停函数执行,next方法可以恢复函数执行。这使得Generator函数非常适合将异步任务同步化。接下来会使用setTimeout来模拟异步任务。
const person = sex => {returnnew Promise((resolve, reject) => { window.setTimeout(() => { const data = { sex,name:'keith', height: 180 } resolve(data) }, 1000) })}function*gen() {const data = yield person('boy') console.log(data)}const g = gen()const next1 = g.next() // {value: Promise,done:false}next1.value.then(data => { g.next(data)})
从上面代码可以看出,第一次调用next方法时,启动了遍历器对象,此时返回了包含value和done属性的对象,由于value属性值是promise对象,因此可以使用then方法获取到resolve传递过来的值,再使用带有data参数的next方法给上一个yield表达式传递返回值。
此时在const data = yield person()这句语句中,就可以得到异步任务传递的参数值了,实现了异步任务的同步化。
但是上面的代码会有问题。每次获取异步的值时,都要手动执行以下步骤
const g = gen()const next1 = g.next() {value: Promise,done:false}next1.value.then(data => { g.next(data)})
上面的代码实质上就是每次都会重复使用value属性值和next方法,所以每次使用Generator实现异步都会涉及到流程控制的问题。每次都手动实现流程控制会显得麻烦,有没有什么办法可以实现自动流程控制呢?实际上是有的: )
thunk函数实现流程控制
thunk函数实际上有些类似于JavaScript函数柯里化,会将某个函数作为参数传递到另一个函数中,然后通过闭包的方式为参数(函数)传递参数进而实现求值。
函数柯里化实现的过程如下
functioncurry (fn) { const args1 = Array.prototype.slice.call(arguments, 1)returnfunction() { const args2 = Array.from(arguments) const arr = args1.concat(args2)returnfn.apply(this, arr) }}
使用curry函数来举一个例子: )
// 需要柯里化的sum函数const sum = (a, b) => {returna + b}curry(sum, 1)(2) // 3
而thunk函数简单的实现思路如下:
// ES5实现const thunk = fn => {returnfunction() { const args = Array.from(arguments)returnfunction(callback) { args.push(callback)returnfn.apply(this, args) } }}// ES6实现const thunk = fn => {returnfunction(...args) {returnfunction(callback) {returnfn.call(this, ...args, callback) } }}
从上面thunk函数中,会发现,thunk函数比函数curry化多用了一层闭包来封装函数作用域。
使用上面的thunk函数,可以生成fs.readFile的thunk函数。
const fs = require('fs')constreadFileThunk = thunk(fs.readFile)readFileThunk(fileA)(callback)
使用thunk函数将fs.readFile包装成readFileThunk函数,然后在通过fileA传入文件路径,callback参数则为fs.readFile的回调函数。
omega-shs.fdcpx.net
omega-bjs.fdcpx.net
omega-shenzhen.biaoshouhou.cn
omega-gzs.biaoshouhou.cn
omega-shs.audemarsweixiu.com
omega-bjs.audemarsweixiu.com
omega-shenzhen.hidcwatch.com
omega-gzs.hidcwatch.com
omega-shs.fjfsx.com
omega-bjs.fjfsx.com
omega-shenzhen.hntwx.cn
omega-gzs.hntwx.cn
omega-shs.hx626.com
omega-bjs.hx626.com
omega-shenzhen.watchjwf.cn
omega-gzs.watchjwf.cn
omega-shs.shjshdzb.com
omega-bjs.shjshdzb.com
omega-shenzhen.shmwatch.cn
omega-gzs.shmwatch.cn
omega-shs.gyjshd.com
omega-bjs.gyjshd.com
omega-shenzhen.zhcxb.cn
omega-gzs.zhcxb.cn
omega-shenzhen.jshdvip.com
omega-gzs.jshdvip.com
omega-shs.gyjshdzb.com
omega-bjs.gyjshdzb.com
omega-sys.jhpwd.cn
omega-zzs.jhpwd.cn
omega-shenzhen.wzjshd.com
omega-gzs.wzjshd.com
omega-shs.jsfltime.com
omega-bjs.jsfltime.com
omega-sys.watchwb.cn
omega-css.watchwb.cn
omega-shenzhen.watch-hdl.com
omega-gzs.watch-hdl.com
omega-shs.watchhdlb.cn
omega-bjs.watchhdlb.cn
omega-whs.watchhdli.cn
omega-nbs.watchhdli.cn
omega-shenzhen.watchrhf.cn
omega-gzs.watchrhf.cn
omega-shs.watchec.cn
omega-bjs.watchec.cn
omega-whs.watchda.cn
omega-xms.watchda.cn
omega-hzs.csjshd.com
omega-njs.csjshd.com
当然,还有一个thunk函数的升级版本thunkify函数,可以使得回调函数只执行一次。原理和上面的thunk函数非常像,只不过多了一个flag参数用于限制回调函数的执行次数。下面我对thunkify函数做了一些修改。