We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同
回顾下上文提到的解决异步的手段:
那么,上文我们提到promsie已经是一种比较流行的解决异步方案,那么为什么还出现Generator?甚至async/await呢?
promsie
Generator
async/await
该问题我们留在后面再进行分析,下面先认识下Generator
执行 Generator 函数会返回一个遍历器对象,可以依次遍历 Generator 函数内部的每一个状态
形式上,Generator 函数是一个普通函数,但是有两个特征:
function
yield
function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; }
Generator 函数会返回一个遍历器对象,即具有Symbol.iterator属性,并且返回给自己
Symbol.iterator
function* gen(){ // some code } var g = gen(); g[Symbol.iterator]() === g // true
通过yield关键字可以暂停generator函数返回的遍历器对象的状态
generator
function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; } var hw = helloWorldGenerator();
上述存在三个状态:hello、world、return
hello
world
return
通过next方法才会遍历到下一个内部状态,其运行逻辑如下:
next
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对应状态值
done
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进行遍历
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
回顾之前展开异步解决的方案:
这里通过文件读取案例,将几种解决异步的方案进行一个比较:
所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,再调用这个函数
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这个文件以后,回调函数才会执行
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) })
这种链式操作形式,使异步任务的两段执行更清楚了,但是也存在了很明显的问题,代码变得冗杂了,语义化并不强
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()); };
将上面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
promise和async/await是专门用于处理异步操作的
Generator并不是为异步而设计出来的,它还有其他功能(对象迭代、控制输出、部署Interator接口...)
Interator
promise编写代码相比Generator、async更为复杂化,且可读性也稍差
async
Generator、async需要与promise对象搭配处理异步情况
async实质是Generator的语法糖,相当于会自动执行Generator函数
async使用上更为简洁,将异步代码以同步的形式进行编写,是处理异步编程的最终方案
Generator是异步解决的一种方案,最大特点则是将异步操作同步化表达出来
function* loadUI() { showLoadingScreen(); yield loadUIDataAsynchronously(); hideLoadingScreen(); } var loader = loadUI(); // 加载UI loader.next() // 卸载UI loader.next()
包括redux-saga 中间件也充分利用了Generator特性
redux-saga
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
The text was updated successfully, but these errors were encountered:
No branches or pull requests
一、介绍
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同
回顾下上文提到的解决异步的手段:
那么,上文我们提到
promsie
已经是一种比较流行的解决异步方案,那么为什么还出现Generator
?甚至async/await
呢?该问题我们留在后面再进行分析,下面先认识下
Generator
Generator函数
执行
Generator
函数会返回一个遍历器对象,可以依次遍历Generator
函数内部的每一个状态形式上,
Generator
函数是一个普通函数,但是有两个特征:function
关键字与函数名之间有一个星号yield
表达式,定义不同的内部状态二、使用
Generator
函数会返回一个遍历器对象,即具有Symbol.iterator
属性,并且返回给自己通过
yield
关键字可以暂停generator
函数返回的遍历器对象的状态上述存在三个状态:
hello
、world
、return
通过
next
方法才会遍历到下一个内部状态,其运行逻辑如下:yield
表达式,就暂停执行后面的操作,并将紧跟在yield
后面的那个表达式的值,作为返回的对象的value
属性值。next
方法时,再继续往下执行,直到遇到下一个yield
表达式yield
表达式,就一直运行到函数结束,直到return
语句为止,并将return
语句后面的表达式的值,作为返回的对象的value
属性值。return
语句,则返回的对象的value
属性值为undefined
done
用来判断是否存在下个状态,value
对应状态值yield
表达式本身没有返回值,或者说总是返回undefined
通过调用
next
方法可以带一个参数,该参数就会被当作上一个yield
表达式的返回值正因为
Generator
函数返回Iterator
对象,因此我们还可以通过for...of
进行遍历原生对象没有遍历接口,通过
Generator
函数为它加上这个接口,就能使用for...of
进行遍历了三、异步解决方案
回顾之前展开异步解决的方案:
这里通过文件读取案例,将几种解决异步的方案进行一个比较:
回调函数
所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,再调用这个函数
readFile
函数的第三个参数,就是回调函数,等到操作系统返回了/etc/passwd
这个文件以后,回调函数才会执行Promise
Promise
就是为了解决回调地狱而产生的,将回调函数的嵌套,改成链式调用这种链式操作形式,使异步任务的两段执行更清楚了,但是也存在了很明显的问题,代码变得冗杂了,语义化并不强
generator
yield
表达式可以暂停函数执行,next
方法用于恢复函数执行,这使得Generator
函数非常适合将异步任务同步化async/await
将上面
Generator
函数改成async/await
形式,更为简洁,语义化更强了区别:
通过上述代码进行分析,将
promise
、Generator
、async/await
进行比较:promise
和async/await
是专门用于处理异步操作的Generator
并不是为异步而设计出来的,它还有其他功能(对象迭代、控制输出、部署Interator
接口...)promise
编写代码相比Generator
、async
更为复杂化,且可读性也稍差Generator
、async
需要与promise
对象搭配处理异步情况async
实质是Generator
的语法糖,相当于会自动执行Generator
函数async
使用上更为简洁,将异步代码以同步的形式进行编写,是处理异步编程的最终方案四、使用场景
Generator
是异步解决的一种方案,最大特点则是将异步操作同步化表达出来包括
redux-saga
中间件也充分利用了Generator
特性还能利用
Generator
函数,在对象上实现Iterator
接口参考文献
The text was updated successfully, but these errors were encountered: