Iterator、Generator、async、await 异步编程
来源:互联网 发布:淘宝网的女款高端衬衣 编辑:程序博客网 时间:2024/06/07 00:29
Iterator、Generator、async、await 异步编程
参考ECMAScript 6 入门
Iterator 遍历器
说明
Iterator(遍历器、迭代器)是一种接口,他为不同的数据结构提供了统一的访问机制(即 for-of循环),任何数据结构只要部署 Iterator=接口,就可以完成遍历操作(即一次处理该数据结构的所有成员)
主要作用
- 为各种数据结构提供统一、简便的访问借口
- 使得数据结构的成员能够按照某种次序排列
- 提供给 for-of 循环使用
一个基本的迭代器的构成
从规范的遍历过程来看
- 迭代器对象创建时,实际是创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用迭代器对象的 next() 方法,指针指向数据结构的第一个成员
- 不断调用 next() 方法 指针依次指向下一个成员,直到指向数据结构的末尾
每一次调用 next() 方法都会返回当前成员的信息,包括 value:值;done:true|false 表示遍历是否结束
const a = [12, 223, 34, 54]; const iter = convertToIterator(a); console.log(iter.next()); // {value: 12, done: false} console.log(iter.next()); // {value: 223, done: false} console.log(iter.next()); // {value: 34, done: false} console.log(iter.next()); // {value: 54, done: false} console.log(iter.next()); // {value: undefined, done: true} function convertToIterator(arr) { let idx = 0; return { next: function () { return idx < arr.length ? {value: arr[idx++], done: false} : {value: undefined, done: true}; } } }
默认的 Iterator 接口
一种数据结构只要部署了 Iterator 接口,就可以成为‘可遍历的’, ES6 的 Iterator 接口部署在数据结构的
Symbol.iterator
上,这个属性本省是一个函数,调用这个函数就会返回一个遍历器对象,遍历器对象具有 next() 方法控制台查看查看
console.log(Array.prototype);
Symbol 对象简介
ES5 中对象的属性名都是字符串,如果给对象添加新方法的名称与旧的相同,就会将之前的属性覆盖掉,Symbol 是 ES6 引入的一种新的原始数据类型(有点类似于字符串的数据类型),表示独一无二的值
Symbol.iterator
是内置的Symbol 值(Symbol(Symbol.iterator)
)指向对象的默认遍历器更多 Symbol 资料查看这里
基本结构
const obj = { [Symbol.iterator] : function () { return { next: function () { return { value: 1, done: true }; } }; } };
具备 Iterator 接口的数据结构
Array | Map | Set | String | TypedArray | 函数的 arguments
调用 Iterator 接口的方法
for-of | 扩展运算符... | 解构赋值 | Array.form
参考了 Iterator 和 for…of 循环
let arr = ['a', 'b', 'c']; let iter = arr[Symbol.iterator](); iter.next() // { value: 'a', done: false } iter.next() // { value: 'b', done: false } iter.next() // { value: 'c', done: false } iter.next() // { value: undefined, done: true }
Generator 函数
Generator 函数是 ES6 提供的一种异步编程的解决方案,语法行为与传统函数完全不同,可以将其理解为一个状态机,内部封装了多个内部状态,执行 Generator 函数会返回一个遍历器对象,返回的遍历器对象可以依次遍历 Generator 函数内部的每一个状态
一个 Generator 函数的基本构成
与普通函数相比
- Generator 函数
function
关键字后要加*
function* name(){}
; - 函数体内部使用
yield
表达式,定义不同的内部状态,最后一个 return 表示返回最后一个状态 - Generator 函数调用与普通函数一样,不同的是 Generator 函数调用后并不执行,返回的也不是函数结果,而是一个指向内部状态的指针,也就是遍历器对象
- 每次通过调用遍历器对象的next() 方法,使指针移动到下一个状态,直到最后一个 return 的状态
- Generator 函数是分段执行的, yield 表达式是暂停执行的标记,而 next 方法可以恢复执行
function* generator () { yield 'first'; yield 'sec'; return 'finish'; } const a = generator(); console.log(a); console.log(a.next()); // {value: "first", done: false} console.log(a.next()); // {value: "sec", done: false} console.log(a.next()); // {value: "finish", done: true} console.log(a.next()); // {value: undefined, done: true}
yield 表达式
yield 表达式是 Generator 函数内部的暂停标志
- 调用 Generator 函数,函数体内代码不会执行
- 调用 next() 方法后,函数体内代码顺序执行,直到遇到 yield 表达式(
yield 111 + 1;
),执行完此表达式,表达式的值就是返回对象的 value - yield 表达式与 return 相似,都能返回后边表达式的值,return 在一个函数里只能执行一次,yield 可以执行很多次,
- yield 表达式(a)如果在另外一个表达式中(b),必须放在括号中,而且 yield 表达式a 执行后的返回值与表达式 b 暂时不会执行,会等到下一次 next 在执行
function* generator () { console.log('1'); yield 111 + 1; console.log('2'); yield 222 + 2; console.log('3'); return 33 + 3; } const a = generator(); // 什么都没有打印 console.log(a); const fir = a.next(); // 1 console.log(fir); // {value: 112, done: false} a.next(); // 2
next 方法的参数
yield 表达式本身没有返回值,或者说总是返回 undefined next 方法可以带一个参数,此参数会被当做 上一个 yield 表达式的返回值,一般第一次调用 next()方法不会输入值,只有第二次传递的参数才是有效的
function* gerenator() { const x = 30; const a = yield x; return a + 10; } const test = gerenator(); const val = test.next().value; const final = test.next(val + 50); console.log(final); // {value: 90, done: true}
for-of 循环
for…of循环可以自动遍历 Generator 函数时生成的Iterator对象,且此时不再需要调用next方法
除了for…of循环以外,扩展运算符(…)、解构赋值和Array.from方法内部调用的,都是遍历器接口。这意味着,它们都可以将 Generator 函数返回的 Iterator 对象,作为参数
function* gerenator() { const x = 30; const a = yield x; const b = yield a; return b + 10; } const test = gerenator(); // for-of 循环 for (let o of test) { console.log(o); // 30 undefined } // 展开操作符 console.log([...test]); // [30, undefined] // Array.from() console.log(Array.from(test)); // [30, undefined] // 解构赋值 let [a, b] = test; console.log(a, b); // 30 undefined
Generator.prototype.throw()
Generator 函数的遍历器对象都由一个 throw() 方法,可以在函数体外抛出错误,然后在函数体内捕获
Generator 函数体外抛出的错误,可以在函数体内捕获;反过来,Generator 函数体内抛出的错误,也可以被函数体外的catch捕获。
var g = function* () { try { yield; } catch (e) { console.log('内部捕获', e); } }; var i = g(); i.next(); try { i.throw('a'); i.throw('b'); } catch (e) { console.log('外部捕获', e); } // 内部捕获 a // 外部捕获 b
Generator.prototype.return()
return 方法 用来终结遍历 Generator 函数,如果 return() 有参数,则返回参数的值
function* gen() { yield 1; yield 2; yield 3; } var g = gen(); console.log(g.next()); // { value: 1, done: false } console.log(g.return('foo')); // { value: "foo", done: true } console.log(g.return('foo')); // { value: undefined, done: true } console.log(g.next()); // { value: undefined, done: true } function* test() { yield 1; yield 2; yield 3; } var t = test(); console.log(t.next()); // { value: 1, done: false } console.log(t.return()); // { value: undefined, done: true }
yield* 表达式
用于在一个 Generator 函数内部调用另外一个 Generator 表达式
- Generator 函数内部直接执行
foo()
外部函数不会得到返回值 - Generator 函数内部执行
yield foo()
外部函数会得到一个遍历器对象 - Generator 函数内部执行
yield* foo()
外部函数会使用这个遍历器
function* foo() { yield 'a'; yield 'b'; } function* bar1() { yield 'x'; yield foo(); yield 'y'; } for (let o of bar1()) { console.log(o); /* x Generator {_invoke: function} y */ } function* bar2() { yield 'x'; yield* foo(); yield 'y'; } for (let o of bar2()) { console.log(o); /* x a b y */ }
作为对象属性的 Generator 函数
// 简写 let obj = { * myGeneratorMethod() { } }; // 等价于 let obj = { myGeneratorMethod: function* () { } };
注意事项
- Generator 函数不是构造函数 不能使用 new 关键字创建对象
- Generator 函数总是返回一个遍历器,而不是一般构造函数的 this 不可以像构造函数一样在函数内部 通过 this 定义属性,但是可以在 函数的 prototype 上定义方法
javascript 传统的异步编程
异步就是指一个任务不是连续完成的,先执行一部分,转而做其他任务,等到做好准备再回来执行接下的部分
- 回调函数
- 事件监听
- 发布/订阅
- Promise
协程
‘协程’的意思是多个线程相互协作,大致流程如下
- 协程 A 开始执行
- 协程 A 执行到一半,进入暂停,执行权转移到协程 B
- 一段时间后 协程 B 交还执行权给 A
- 协程 A 继续执行
Generator 函数是携程在 ES6 的实现,最大的特点就是可以交出函数的执行权,整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器,异步操作需要暂停的地方都用 yield 语句注明
function change() { return 'change'; } function* test() { const a = yield change(); // 执行权交给 change 函数 yield a + ' something'; } const ts = test(); const ret = ts.next(); console.log(ret.value); // change console.log(ts.next(ret.value).value); // change something
Generator 函数的数据交换和错误处理
- 向外输出数据 通过 next() 返回值的 value 属性
- 向内注入数据 通过 next(sth) 方法添加参数
- Generator 函数内可以部署错误处理代码,捕获函数体外抛出的错误(generator.throw())
async 函数
async 函数由 ES2017 引入,为了更加方便异步操作,它其实就是 Generator 函数的语法糖
async 函数 与 Generator 函数的差异
- 内置执行器,Generator 函数执行必须靠执行器或者调用 next() 方法,而 async 函数自带执行器,async 函数的调用与普通函数一样
- 更好的语义:显而易见 async(异步)await(等待)这样的组合更容易表示
- await 命令后边可以是 Promise 对象或者原始类型的值(原始类型的值,会等同于同步操作)
- async 函数返回值是 Promise 类型的可以用 then 方法指定下一步操作
async function timeout(ms) { await new Promise(function (resolve) { setTimeout(resolve, ms); }) } async function asyncPrint (value, ms) { await timeout(ms); console.log(value); } asyncPrint('hahaha', 1000);
async 函数的多种使用形式
// 函数声明 async function fn () {} // 函数表达式 const fn = async function () {} // 对象的方法 const obj = { async fn () {} } // Class 方法 class Fn { async getFn() { } } // 箭头函数 const fn = async () => {};
async 函数的返回值
async 函数返回一个 Promise 对象,这个对象可以调用 then() 方法 进行下一步的操作,但是如果需要上一步的返回值则需要在 async 函数内 return
async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到
const obj = { name: 'obj name', age: 'obj age', async timeout(ms) { const age = await new Promise((resolve) => { setTimeout(() => { resolve(this.age); }, ms) }) // 返回给下一步的参数 return this.name + ' ' + this.age; } } async function asyncPrint (value, ms) { const info = await obj.timeout(ms); console.log(value, info); // 抛出错误 throw new Error('wrong'); } asyncPrint('hahaha', 1000) // hahaha obj name obj age .then(res => { console.log('right', res); }) .catch(err => { console.log(err); // 错误被捕获 Error: wrong })
一个 async 函数返回的 Promise 对象,必须等到内部素有的 await 执行完成后,才会发生状态改变,除非遇到 return 语句 或者抛出错误
await 命令
- 正常情况下 await 命令后是一个 Promise 对象,如果不是,会被转变成一个立即 resolve 的 Promise 对象
- await 命令后边的 Promise 对象如果为 reject 状态,则 reject 的参数会被 catch 方法的回调函数接收
- 只要一个 await 语句后面的 Promise 变为 reject 那么整个 async 函数都会中断执行
- 通过 try-catch 结构可以避免一个异步操作失败影响后边异步操作执行
// 1. 正常情况下 await 命令后是一个 Promise 对象,如果不是,会被转变成一个立即 resolve 的 Promise 对象 async function fn() { return await 222; } fn().then(res => console.log(res)); // 222; // 2. await 命令后边的 Promise 对象如果为 reject 状态,则 reject 的参数会被 catch 方法的回调函数接收 // 3. 只要一个 await 语句后面的 Promise 变为 reject 那么整个 async 函数都会中断执行 async function fn() { await Promise.reject('wrong'); return await 222; // 不会执行 } fn() .then(res => console.log(res)) // 不执行 .catch(err => console.log('error', err)); // error wrong // 4. 通过 try-catch 结构可以避免一个异步操作失败影响后边异步操作执行 async function fn() { try { await Promise.reject('wrong'); } catch(e) { console.log('catch error', e); // catch error wrong } return await 222; } fn() .then(res => console.log(res)) // 222; .catch(err => console.log('error', err)); // 不执行1 // 在可能会抛出错误的Promise 后边接上 catch() 也可以达到同样的效果 async function fn() { await Promise.reject('wrong').catch(err => console.log('catch', err)); // catch wrong return await 222; } fn() .then(res => console.log(res)) // 222; .catch(err => console.log('error', err)); // 不执行
注意事项
1. 如果了两个异步操作没有互相依赖关系,可以让他们同时触发
// 写法一 let [foo, bar] = await Promise.all([getFoo(), getBar()]); // 写法二 let fooPromise = getFoo(); let barPromise = getBar(); let foo = await fooPromise; let bar = await barPromise;
2. await 命令只能用在 async 函数中,如果 用在普通函数中就会报错
async function fn() { const arr = [11, 232, 33]; arr.forEach((item) => { await new Promise((resolve) => { resolve(item) }) }) return await 222; } fn() .then(res => console.log(res)) // 报错
更多文章
- ECMAScript 6 常用特性整理
- Promise-使用整理
- Decorator装饰器
- Iterator、Generator、async、await 异步编程
- Iterator、Generator、async、await 异步编程
- async/await 异步编程
- async/await异步编程
- Async和await异步编程
- Python异步编程Async/Await
- C#异步编程async await
- 异步编程中的最佳做法(Async/Await)
- 使用 Async 和 Await 的异步编程
- Async和Await异步编程的原理
- Async/Await 异步编程中的最佳做法
- async和await实现异步编程
- .NET 中的 async/await 异步编程
- .NET 中的 async/await 异步编程
- C# Async/await 异步多线程编程
- C#异步编程之async、await
- 初识C#异步编程Task,await,async
- .NET 中的 async/await 异步编程
- C#异步编程和await/async
- 0基础学习向GitHub 提交代码
- 【HDU1215】七夕节
- qq第三方登录
- 最新版支付宝电脑网站支付原理及yii2中集成使用教程
- React.createElement: type is invalid -- expected a string (for built-in components) or a class/funct
- Iterator、Generator、async、await 异步编程
- 写一个函数,他的原型是 int continumax(char*outoutstr,char*intputstr).
- swift3.0 入门学习笔记之一 基础篇
- Android DatePicker,NumPicker解析(分割线颜色及调整大小)
- Dinic模板(最大流最小割)
- Ble分包发送
- 在Linux上安装TomCat
- Java 连接mysql数据库warn:Establishing SSL connection without server's identity verification is not ...
- PHP Input/Output缓冲区