JS高级—12—可迭代对象与迭代器;生成器函数与生成器;async与await;
一、迭代器
二、可迭代对象
2.1什么是可迭代对象
2.2可迭代对象的作用,
通过可迭代对象可以很方便的生成一个新的迭代器;
for of循环就是可迭代对象才有的;forof就是一个语法糖,本质上就是调用迭代器的next()方法;
- 注意:new Set是,传入的参数也要是一个可迭代对象;
- 注意:{}不是可迭代对象,但是为什么也可以用spread snbtax和解构赋值,因为在es9中ecma要求所有的浏览器引擎实现这个“特性”,即使{}不是可迭代对象也要实现这个功能;当然这个功能不用迭代器也很容易实现;总的来说,到目前为止{}仍然不是可迭代对象;
2.3自定义类的迭代
2.4迭代器的中断
通过调用break等,会调用迭代器的return方法;
三、生成器
生成器就是一个生成器函数放回的变量,这个变量是特殊的迭代器;
function* foo() { console.log(111) yield bar(); console.log(333) } function bar() { console.log(2222) return Promise.resolve('helloworld'); } generator = foo(); generator.next() console.log(444)
结果是1111 2222 4444
所以说:yied和await一样的,也是会去执行后面跟着的函数里的代码的,比如这里就是执行了bar()里的222;
只是遇到了resolve后才会让出控制权,等promise变成fullfilled态后会继续拿到js线程的控制权;
yield bar() 和bar()
所以说:yield和单纯的promise.then()方法还是不一样的,.then()方法会把回调函数加入到微任务队列,然后继续执行本函数下面的代码,但如果是yield,则不仅.then()方法会把回调函数加入到微任务队列,yield地下的代码也无法执行也应该被加入到微任务队列了;
至于原因,那可能就是ecma的规范如此定义的;
四、生成器函数
4.1概念
调用生成器函数会返回一个生成器,
调用生成器的next方法,可以控制函数执行到下一个yield,并且yield后面跟的value会作为生成器的next方法返回值,格式为{value:'yield后面跟着value', done: false};
4.2生成器next方法提供参数
我们给生成器的next()方法传递一个参数,在生成器函数中,可以痛哟const n= yield initial 接受,n就是我们传递的参数;
4.3生成器return方法
4.4生成器throw方法
如果有try catch,那么捕获到异常后后续的代码可以继续运行;
如果没有,那么就是直接抛出异常了,程序终止即js线程终止了;
注意第二个有问题,在catch中也可以继续放置yield;
五、生成器替代迭代器
5.1概念
因为生成器函数会自动返回一个生成器,又因为生成器就是一种特殊的迭代器,所以我们可以使用生成器替代迭代器,使用生成器函数自动帮助我们生成生成器;
加了一个星号*,表示这个函数是生成器函数,生成器函数一定会返回生成器,可以调用生成器的next方法;
加了一个async,表示这个函数是异步函数,异步函数一定会返回一个promise(这个promise的状态依据异步函数是返回基本数值还是新的promise还是thenable接口而定),可以调用promise的then方法;
yield* 表示的意思是后面必须跟一个可迭代对象,然后它会不停的把可迭代对象的元素遍历出来;
这是一个语法糖,我们可以不用写这种代码了
for ( item of arr){ yield item }
等价于
yield* arr
5.2自定义类的可迭代对象
5.3promise的异步解方案
第一种:回调地狱
第二种:链式调用
第三种:是最好的,asyncawait其实就是第三种的语法糖;
// request.js function requestData(url) { // 异步请求的代码会被放入到executor中 return new Promise((resolve, reject) => { // 模拟网络请求 setTimeout(() => { // 拿到请求的结果 resolve(url) }, 2000); }) } // 需求: // 1> url: why -> res: why // 2> url: res + "aaa" -> res: whyaaa // 3> url: res + "bbb" => res: whyaaabbb // 1.第一种方案: 多次回调 // 回调地狱 // requestData("why").then(res => { // requestData(res + "aaa").then(res => { // requestData(res + "bbb").then(res => { // console.log(res) // }) // }) // }) // 2.第二种方案: Promise中then的返回值来解决 // requestData("why").then(res => { // return requestData(res + "aaa") // }).then(res => { // return requestData(res + "bbb") // }).then(res => { // console.log(res) // }) // 3.第三种方案: Promise + generator实现 function* getData() { const res1 = yield requestData("why") const res2 = yield requestData(res1 + "aaa") const res3 = yield requestData(res2 + "bbb") const res4 = yield requestData(res3 + "ccc") console.log(res4) } // function* getDepartment() { // const user = yield requestData("id") // const department = yield requestData(user.departmentId) // } // 3.1> 手动执行生成器函数 const generator = getData() generator.next().value.then(res => { generator.next(res).value.then(res => { generator.next(res).value.then(res => { generator.next(res) }) }) }) // 3.2> 自己封装了一个自动执行的函数 !!当你不知道要执行多少次的时候,用递归; // function execGenerator(genFn) { // const generator = genFn() // function exec(res) { // const result = generator.next(res) // if (result.done) { // return result.value // } // result.value.then(res => { // exec(res) // }) // } // exec() // } // execGenerator(getData) // execGenerator(getDepartment) // 3.3> 第三方包co,就是我们写的自动执行函数,npm install安装后可以直接使用; // TJ: co/n(nvm)/commander(coderwhy/vue cli)/express/koa(egg) // const co = require('co') // co(getData)
// 4.第四种方案: async/await async function getData() { const res1 = await requestData("why") const res2 = await requestData(res1 + "aaa") const res3 = await requestData(res2 + "bbb") const res4 = await requestData(res3 + "ccc") console.log(res4) } getData()
六、async和await
一个函数加了个async表示,这个函数是异步函数,函数体里会有异步操作。那么异步函数的执行流程是什么样的?
6.1概念
await后面要跟一个 返回promise的表达式
6.2异步函数的执行流程
咩有await时和普通函数一样,都是同步执行;
6.3await关键字