Generator
语法
function* generator() {
yield 1;
yield 2;
yield 3;
}
const gen = generator()
console.log(gen.next()) // { value: 1, done: false }
console.log(gen.next()) // { value: 2, done: false }
console.log(gen.next()) // { value: 3, done: false }
TIP
Generator
函数是 ES6 提供的一种异步编程解决方案, 执行 Generator 函数会返回一个可迭代对象(Iterator Object), Iterator
的主要方法是next()
, next() 方法返回一个包含属性 done 和 value 的对象。你也可以通过向 next 方法传入一个参数来向生成器(generator)传一个值.
调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行.
给对象自定义迭代器对象
js中对象不可被for...of
遍历, 我们可以给对象实现一个迭代器函数,供for..of
调用。 提供一个 generator
函数作为 Symbol.iterator
,来使用 generator 进行迭代。
之所以代码正常工作,是因为 range[Symbol.iterator]()
现在返回一个 generator,而 generator 方法正是 for..of
所期望的:
- 它具有
.next()
方法 - 它以
{value: ..., done: true/false}
的形式返回值
const range = {
from: 1,
to: 5,
*[Symbol.iterator]() { // [Symbol.iterator]: function*() 的简写形式
for(let value = this.from; value <= this.to; value++) {
yield value
}
}
}
for(let v of range) {
console.log(v, 'range')
}
// 1 range
// 2 range
// 3 range
// 4 range
// 5 range
参考资料
async/await
promise解决了遗留已久的“回调地狱”的问题,但是当链式调用过多时,我们似乎又陷入了另一种麻烦里,看起来也不是很优雅、直观,因此async/await
这种解决方法应运而生。它让我们能够更愉快的使用promise。
async函数
语法:
/* 隐式 */
async function fn() {
return 1
}
/* 显示 */
async function fn2() {
return new Promise(resolve => resolve(2))
}
fn().then(res => {
console.log(res) // 1
})
fn2().then(res => {
console.log(res) // 2
})
在函数前加上async关键字意味着函数总会返回一个promise。如果函数没有显示的返回promise,js引擎也会隐式的将其封装到一个promise中,如果函数有返回值,这个值就会被resolved。
await关键字
await 关键字会使js引擎等待,直到 promise 得到解决并返回其结果。await必须且只能在async函数中使用。
async function fn() {
const promise = new Promise(resolve => {
setTimeout(() => {
resolve('done')
}, 2000)
})
const res = await promise
console.log(res) // 2秒后打印 done
console.log('start')
}
fn()
处理错误:如果promise没有正常的resolve,就会被rejected,抛出错误(类似throw语句),此时await就不会返回结果,需要我们去处理错误。
/**
* 一般我们会用try/catch处理
* 处理同步错误: await会同步执行代码
*/
async function fn() {
const promise = Promise.reject(new Error('Error: error~'))
try {
await promise
/*与 throw new Error('error~') 同理*/
} catch (error) {
console.log(error) // Error: error
}
}
fn()
/* 或者用.catch() */
async function fn2() {
return Promise.reject(new Error('error---'))
}
fn2().catch(err => {
console.log(err) // Error: error---
})
另外:一个async函数中,能够有多个await执行。
使用Promise 和 Generator 实现async函数
TIP
实际上,async函数就是利用Promise
和 Generator
实现的,可以理解为js在语言层面提供的语法糖。
模拟实现
Details
function asyncToGenerator(genFn) {
//把返回值包装成promise
return new Promise((resolve, reject) => {
var gen = genFn()
function step(val) {
//错误处理
try {
var next = gen.next(val)
} catch(err) {
return reject(err)
}
// 执行结束则退出执行函数,返回最终执行结果
if(next.done) {
return resolve(next.value)
}
//next.value需包装为promise,以兼容yield后面是非promise的情况(如基本类型)
Promise.resolve(next.value).then(
val => {
console.log(val, 'step then')
step(val)
},
err => {
//抛出错误,供genFn函数内捕获错误
console.log(err, 'step catch')
gen.throw(err)
})
}
step()
})
}
// test case
function* gen() {
try {
yield Promise.resolve(1)
yield Promise.resolve(2)
yield Promise.reject('yield error')
yield Promise.resolve(4)
} catch (error) {
console.log(error, 'gen catch')
}
}
asyncToGenerator(gen).then(res => {
console.log(res)
})
// log
// 1 step then
// 2 step then
// yield error step catch
// yield error gen catch