了解generator这篇文章就够了
Generator介绍
- generator函数是es6提出的一种异步编程的方案,可以用来替代promise来获取异步执行的结果
Generator语法
- Generator函数两个特征
- 普通函数加
星号*
的模式,至于星号的位置没有什么规定的要求// 普通函数 function fn(){} // generator函数 function * fn(){} function* fn(){} // 推荐 function *fn(){} function*fn(){}
- 函数内部使用yield关键字,yield代表
产出
的意思- 注意:普通函数里面无法直接使用yield,否则会出现报错
// 普通函数内部无法使用yield function fn(){ yield 1; yield 2; yield 3; } fn() // 调用的时候会报错 Uncaught SyntaxError: Unexpected number // 带*号的函数内部可以使用yield function* fn(){ yield 1; yield 2; yield 3; } fn() // 不会报错
- 普通函数加
- Generator函数的返回值
- 普通函数的返回值看函数内部有没有写return,return后面的数据就是函数的返回值
// 没有return function fn(){} console.log(fn()) // undefined // 有return function fn(){ return 1 } console.log(fn()) // 1
- generator函数即使没有写return返回的也是一个
遍历器对象
,什么是遍历器对象呢,我们暂时可以理解这是一个带有特殊方法的对象// 没有return function* fn(){} console.log(fn()) // 返回一个遍历器对象 // 有return function* fn(){ return 1 } console.log(fn()) // 返回也是一个遍历器对象
- 普通函数的返回值看函数内部有没有写return,return后面的数据就是函数的返回值
- next方法
- 每一个generator函数调用完成之后都会返回一个遍历器对象,该对象可以调用next()方法
- 每一次遍历器对象调用next()方法之后都会返回一个新对象,返回的对象里面有两个属性
value
代表next方法执行的结果done
代表遍历器是否执行结束
function* fn(){} var it = fn() console.log(it.next()) // {value: undefined, done: true}
- 如果generator函数有写return的话,那么返回的遍历器对象调用next方法就有一个value值,就是return返回的结果,但是这个遍历器还是执行一次就结束了
function* fn(){ return 1 } var it = fn() console.log(it.next()) // {value: 1, done: true}
- 如何让一个遍历器多次执行呢?我们可以在generator函数里面使用刚才讲的yield表达式,这样我们就可以多次调用遍历器的next方法
- 第一次调用next()方法,返回一个对象,该对象的value值是第一个yield表达式之后的值,返回done是false,代表遍历器没有执行结束
- 第二次调用next()方法,返回一个对象,该对象的value值是第二个yield表达式之后的值,返回done是false,代表遍历器没有执行结束
- 第三次调用next()方法,返回一个对象,该对象的value值是第三个yield表达式之后的值,返回done是false,代表遍历器没有执行结束
- 第四次调用next()方法,返回一个对象,该对象的value值是undefined,返回done是true,代表遍历器执行结束
function* fn(){ yield 1 yield 2 yield 3 } var it = fn() // it是返回的遍历器 // it遍历器当使用yield之后可以多次调用 console.log(it.next()) // {value: 1, done: false} console.log(it.next()) // {value: 2, done: false} console.log(it.next()) // {value: 3, done: false} console.log(it.next()) // {value: undefined, done: true}
- next()方法传参
- yield表达式执行完成之后没有或者说每一次默认的返回值为undefined
function* fn(){ var x = yield 1 console.log(x) // undefined var y = yield 2 console.log(y) // undefined var z = yield 3 console.log(z) // undefined } var it = fn() console.log(it.next()) console.log(it.next()) console.log(it.next()) console.log(it.next())
- 每一次调用next方法传递的参数就是上一个yield的返回值
function* fn(){ var x = yield 1 console.log(x) // 10 var y = yield 2 console.log(y) // 20 var z = yield 3 console.log(z) // 30 } var it = fn() console.log(it.next()) console.log(it.next(10)) console.log(it.next(20)) console.log(it.next(30))
- 注意: 第一次调用next无法传参,及时传了也没有任何作用,因为第一次next方法相当于是启动整个generator的函数,如果需要第一次调用next传参,需要在generator函数外面再包裹一层
function parent(){ function* gen(){ var x = yield 1 console.log(x) var y = yield 2 console.log(y) var z = yield 3 console.log(z) } // 相当于在parent里面完成对generator函数的第一次next调用 var it = gen() it.next() return it } // 这样我们再次使用generator的时候 就可以传递参数了 var it = parent() console.log(it.next(10)) console.log(it.next(20)) console.log(it.next(30))
- yield表达式执行完成之后没有或者说每一次默认的返回值为undefined
- throw方法
- throw方法是遍历器对象的另一种方法,作用和next相同,不同的地方就是throw是抛出异常错误,一旦异常抛出,在generator函数里面对应的yield语法执行的时候就可以捕获异常,yield下面的语句就不会往下运行了
function* fn() { var x = yield 1 console.log(x) // 10 var y = yield 2 // 程序执行到此处会报错,就不会在往下执行了 下一个next就不会被调用 console.log(y) var z = yield 3 console.log(z) } var it = fn() console.log(it.next()) console.log(it.next(10)) console.log(it.throw(20)) // 此处抛出异常 console.log(it.next(30))
- throw方法是遍历器对象的另一种方法,作用和next相同,不同的地方就是throw是抛出异常错误,一旦异常抛出,在generator函数里面对应的yield语法执行的时候就可以捕获异常,yield下面的语句就不会往下运行了
- return方法
- return方法也是遍历器对象的一种方法,作用和next相同,不同的地方就是return方法相当于提前结束generator函数的执行,后面再次执行遍历器next方法的结果就不是yield后面的结果,全部为undefined
function* fn() { var x = yield 1 console.log(x) // 10 var y = yield 2 // 程序执行到此处会报错,就不会在往下执行了 下一个next就不会被调用 console.log(y) var z = yield 3 console.log(z) } var it = fn() console.log(it.next()) // {value: 1, done: false} console.log(it.return(10)) // {value: 10, done: true} console.log(it.next(20)) // {value: undefined, done: true} console.log(it.next(30)) // {value: undefined, done: true}
- return方法也是遍历器对象的一种方法,作用和next相同,不同的地方就是return方法相当于提前结束generator函数的执行,后面再次执行遍历器next方法的结果就不是yield后面的结果,全部为undefined
generator在异步任务中的使用
- 没有使用generator导致的地狱回调
ajax('./serve/a.php?num1=25', function (res) { ajax(`./serve/b.php?num2=${res}`, function (res2) { ajax(`./serve/c.php?num3=${res2}`, function (res3) { console.log(res3) }) }) })
- 使用generator
var it = gen() it.next() function* gen(){ var res1 = yield ajax('./serve/a.php?num1=25', function(res){ it.next(res) }) var res2 = yield ajax(`./serve/b.php?num2=${res1}`, function(res){ it.next(res) }) yield ajax(`./serve/c.php?num3=${res2}`, function(res){ console.log(res) }) }