生成器
生成器是一种特殊的迭代器;是ES6中新增的一种函数控制、使用的方案,它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等。
生成器函数
js的使用的函数,函数的终止条件通常是返回值或者抛出了异常
生成器函数也是一个函数,但是和普通函数有一定的区别:
- 语法上:在function后加“*”;
- 生成器函数可以使用“yield”关键字来控制函数的执行流程;
- 生成器函数返回一个生成器;
- 单独的函数调用不会有任何结果,需要使用变量来接收函数返回的生成器。
生成器一般和生成器函数一起使用:
简单示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function* foo() { console.log(1); yield 1; yield 2; console.log(3); yield 3; return 'foo' } const iterator = foo()
console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next());
|
next方法的传值以及yield的返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function* foo(num) { console.log(num); console.log(num); const count1 = yield 1; console.log('第一次yield的返回值,由第二次next传入的参数决定', count1); console.log(2 * count1); yield 2; console.log(3); const count3 = yield 3; console.log('第三次yiled的返回值,由第四次next传入的参数决定', count3); return 'foo' } const iterator = foo(5)
console.log(iterator.next()); console.log(iterator.next(10)); console.log(iterator.next()); console.log(iterator.next(20));
|
注意:第一次调用next方法很少传入参数
生成器其他方法
return
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| function* foo() { const count1 = yield 1; console.log(count1); const count2 = yield 2; return count2; }
let iterator = foo(1); console.log(iterator.next());
console.log(iterator.return('return'));
function* foo() { const count1 = yield 1; console.log(count1); return count1; const count2 = yield 2; return count2; } let iterator = foo(1); console.log(iterator.next()); console.log(iterator.return('return'));
|
throw
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function* foo() { try { const count1 = yield 1; console.log(count1); } catch (error) { console.log('error', error); yield '错误捕获到之后继续执行' } console.log('代码继续执行'); const count2 = yield 2; return count2; }
let iterator = foo(); console.log(iterator.next()); console.log(iterator.throw('iterator throw')); console.log(iterator.next());
|
生成器替代迭代器
使用循环来yield数据
迭代器的目的是返回一个带有next方法的对象
但是,生成器也是一种迭代器;我们可以使用生成器函数直接返回一个生成器,来调用next方法
比如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function createIterator(arr) { let index = 0; return { next: function () { return index < arr.length ? { value: arr[index++], done: false } : { done: true, value: undefined }; } } } let arr = [1, 2, 3] let iterator = createIterator(arr); console.log(iterator.next()); console.log(iterator.next());
function* createIterator(arr) { for (const item of arr) { yield item } } let arr = [1, 2, 3, 4, 5]; const iterator = createIterator(arr); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next());
|
yield*
事实上我们也可以使用给yield*来生产一个可迭代对象;
这个时候相当于是yield的语法糖,只不过会一次迭代这个可迭代对象,每次迭代其中一个值。
(yield* 之后要加上一个可迭代对象)
1 2 3 4 5 6 7
| function* createIterator(arr) { yield* arr } let arr = [1, 2, 3, 4, 5]; const iterator = createIterator(arr); console.log(iterator.next()); console.log(iterator.next());
|
其他例子
示例1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| function createRangeIterator(start, end) { let index = start; return { next: function () { if (index <= end) { return { done: false, value: index++ }; } else { return { done: true, value: undefined }; } } }; }
let iterator = createRangeIterator(10, 20); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next());
function* createRangeIterator(start, end) { let index = start; while (index < end) { yield index++; } }
let iterator = createRangeIterator(10, 20); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next());
|
示例2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
|
class Room { constructor(address, name) { this.address = address; this.name = name; this.students = []; } pushStudent(student) { this.students.push(student); } [Symbol.iterator]() { let index = 0; return { next: () => { if (index < this.students.length) { return { done: false, value: this.students[index++] } } else { return { done: true, value: undefined } } }, } } }
*[Symbol.iterator]() { let index = 0; while (index < this.students.length) { yield this.students[index++]; } } let room = new Room('行远楼A105', '软件工程教室'); room.pushStudent('hrm'); room.pushStudent('xcl'); room.pushStudent('dmy'); room.pushStudent('gq');
let iterator = room[Symbol.iterator](); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next());
for (let student of room) { console.log(student); }
|
生成器向async_await的过渡
异步代码的处理方案
现有一个场景,我们有一个网络请求函数,需要用这个网路函数发送三次网络请求,第二次的请求参数是第一次请求的结果
原始方案(回调函数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function requestData(params, successCallback, failCallback) { setTimeout(function () { successCallback(params + 1); }, 100) }
requestData(1, (res) => { requestData(res, (res2) => { requestData(res2, (res3) => { console.log(res3); }) }) })
|
改进
1 2 3 4 5 6 7 8
| function requestData(params) { return new Promise((reslove, reject) => { setTimeout(function () { reslove(params + 1) }, 100) }) }
|
promise.then
1 2 3 4 5 6 7 8
| requestData(1).then((res) => { requestData(res).then((res2) => { requestData(res2).then((res3) => { console.log(res3); }) }) })
|
promise.then返回新的promise
1 2 3 4 5 6 7
| requestData(1).then((res) => { return requestData(res) }).then((res2) => { return requestData(res2) }).then((res3) => { console.log(res3); })
|
promise + generator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| function requestData(params) { return new Promise((reslove, reject) => { setTimeout(function () { reslove(params + 1) }, 100) }) }
function* getData() { const res = yield requestData(1); const res2 = yield requestData(res); yield requestData(res2); }
const genetator = getData();
genetator.next().value.then((res) => { genetator.next(res).value.then((res2) => { genetator.next(res2).value.then((res3) => { console.log(res3); }) }) })
|
promise+generator函数自动化执行封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| function requestData(params) { return new Promise((reslove, reject) => { setTimeout(function () { reslove(params + 1) }, 100) }) }
function* getData() { const res = yield requestData(1); const res2 = yield requestData(res); const res3 = yield requestData(res2); console.log(res3); }
function execGenerator(genetatorFn) { let generator = genetatorFn(); function exec(res) { const result = generator.next(res); if (result.done) { return result.value; } result.value.then(res => { exec(res) }) } return exec() } execGenerator(getData);
|
参看npm依赖:co
开发者:TJ(TJ Holowaychuk)
该作者对前端的贡献非常大,例如:
npm三方依赖
- n(node版本管理工具)
- commander(vue-cli中使用)
express
koa(egg是基于koa的)
co的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const co = require('co'); function requestData(params) { return new Promise((reslove, reject) => { setTimeout(function () { reslove(params + 1) }, 100) }) }
function* getData() { const res = yield requestData(1); const res2 = yield requestData(res); const res3 = yield requestData(res2); console.log(res3); return res3; } co(getData).then((res) => { console.log(res); });
|
async_await
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function requestData(params) { return new Promise((reslove, reject) => { setTimeout(function () { reslove(params + 1) }, 100) }) }
async function getData() { let res1 = await requestData(1); let res2 = await requestData(res1); let res3 = await requestData(res2); return res3 } getData().then((res) => { console.log(res); })
|
async_await
async:
异步,用来声明一个异步函数,异步函数的返回值一定是一个promise
这个promise的then方法调用时机:异步函数返回了值
sync:与async对立,表示同步
异步函数的返回值
返回一个普通值
js会将返回的普通值用prosmie进行包裹
1 2 3 4 5 6 7
| async function foo() { return 'foo' }
foo().then((res) => { console.log(res); })
|
返回一个实现了then方法的对象
如果这个返回了一个thenable对象(即该对象实现了then方法),promise.then方法的相关回调由该对象的then方法决定
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| async function foo() { return { then(resolve, reject) { resolve('obj then method resolve'); } } }
foo().then((res) => { console.log(res); }, (err) => { console.log(err); })
|
返回一个新的promise
返回一个新的promise,会进行状态移交,会根据返回的新的promise进行相关回调
1 2 3 4 5 6 7 8 9 10 11
| async function foo() { return new Promise((resolve, reject) => { resolve('new promise resolve') }) }
foo().then((res) => { console.log(res); }, (err) => { console.log(err); })
|
async函数抛出异常
异步函数抛出的异常会被作为返回的prosmise的err值,用catch、或者用then的第二个回调来捕获该错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| async function foo() { throw new Error('error') }
foo().then((res) => { console.log(res); }, (err) => { console.log('then的第二个回调捕获', err); })
foo().catch((err) => { console.log('catch捕获', err); })
|
await
在异步函数内部可以使用await关键字
await之后跟表达式(基本使用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function foo() { return new Promise((resolve, reject) => { setTimeout(() => { resolve('result') }, 500) }) }
async function getData() { let res1 = await foo(); let res2 = await foo(); console.log('拿到res1,res2的值', res1, res2); return [res1, res2]; }
getData().then(data => { console.log(data); })
|
实际上,上述的getData函数修改为普通函数时,是这样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function foo() { return new Promise((resolve, reject) => { setTimeout(() => { resolve('result') }, 500) }) } function getData() { return new Promise((resolve, reject) => { foo().then((res1) => { foo().then((res2) => { resolve([res1, res2]) }) }) }) }
getData().then(data => { console.log(data); })
|
async配饰await的使用,使得异步代码看起来时同步代码的样子,实则是异步代码的一种语法糖
await跟新的promise
这种情况适用于await之后跟表达式
1 2 3 4 5 6 7 8 9 10 11
| async function foo() { let res = await new Promise((resolve, reject) => { resolve('await new promise resolve') }) return res }
foo().then((res) => { console.log(res); })
|
await之后跟普通值
如果await之后跟上是一个普通值,会立即返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| async function foo() { let res = await 1; return res }
foo().then((res) => { console.log(res); })
async function foo() { let res = await new Promise((resolve, reject) => { resolve(1) }) return res }
foo().then((res) => { console.log(res); })
|
await之后跟thenable对象
1 2 3 4 5 6 7 8 9 10 11 12 13
| async function foo() { let res = await { then(resolve, reject) { resolve('await obj then resolve') } } return res }
foo().then((res) => { console.log(res); })
|
await:reject值
以上情况,均resolve了值,如果reject之后,会有什么情况呢?
此时需要使用catch来进行捕获
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function requestData(params) { return new Promise((reslove, reject) => { setTimeout(function () { reject('error') }, 100) }) }
async function getData() { let res1 = await requestData(); return res1; }
getData().then(undefined, (err) => { console.log(err); })
|
或者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function requestData(params) { return new Promise((reslove, reject) => { setTimeout(function () { reject('error') }, 100) }) }
async function getData() { try { let res = await requestData(); console.log(res); } catch (error) { console.log(error); } } getData()
|