javascript异步编程之generator(生成器函数)与asnyc/await语法糖
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了javascript异步编程之generator(生成器函数)与asnyc/await语法糖相关的知识,希望对你有一定的参考价值。
Generator 异步方案
相比于传统回调函数的方式处理异步调用,Promise
最大的优势就是可以链式调用解决回调嵌套的问题。但是这样写依然会有大量的回调函数,虽然他们之间没有嵌套,但是还是没有达到传统同步代码的可读性。如果以下面的方式写异步代码,它是很简洁,也更容易阅读的。
// like sync mode
try
const value1 = ajax(/api/url1)
console.log(value1)
const value2 = ajax(/api/url1)
console.log(value2)
const value3 = ajax(/api/url1)
console.log(value3)
const value4 = ajax(/api/url1)
console.log(value4)
const value5 = ajax(/api/url1)
console.log(value5)
catch(err)
console.log(err)
在ES2015
提供了生成器函数(Generator Function)
它与普通函数的语法差别在于,在function
语句之后和函数名之前,有一个“*”作为生成器函数的标示符。
在我们去调用生成器函数的时候他并不会立即去执行这个函数,而是会得到一个生成器对象,直到我们手动调用对象的next
方法,函数体才会开始执行,我们可以使用关键字yield
去向外返回一个值,我们可以在next
方法的返回值中去拿到这个值。另外再返回的属性中还有一个done
关键字来表示生成器是否执行完了,
yield
不会像return
一样去结束函数的执行,只是暂停函数的执行,直到外接下一次调用next
方法时才会继续从yield
位置往下执行
function * foo ()
console.log(start)
yield foo
const generator = foo()
const result = generator.next()
调用next
方法的时候传入了参数的话,所传入的参数会作为yield
关键字的返回值
function * foo ()
console.log(start)
// 我可以在这里接收next传入的参数
const res = yield foo
console.log(res) // 这是我传入的参数
const generator = foo()
const result = generator.next(这是我传入的参数)
console.log(result) // value: foo, done: false
如果我们调用了生成器函数的throw
方法,这个方法会给生成器函数内部抛出一个异常
function * foo ()
console.log(start)
// 我可以在这里接收next传入的参数
try
const res = yield foo
console.log(res) // 这是我传入的参数
catch (err)
console.log(err.message) // 抛出错误
const generator = foo()
const result = generator.next(这是我传入的参数)
console.log(result)
generator.throw(new Error(抛出错误))
利用生成器函数和Promise
来实现异步编程的体验
function ajax(url)
return new Promise((resove, reject) =>
var xhr = new XMLHttpRequest()
xhr.open(GET, url)
// 新方法可以直接接受一个j对象
xhr.responseType = json
xhr.onload = function ()
if (this.status === 200)
resove(this.response)
else
reject(new Error(this.statusText))
xhr.send()
)
function* main()
const user1 = yield ajax(/json1.json)
console.log(user1)
const user2 = yield ajax(/json2.json)
console.log(user2)
const user3 = yield ajax(/json3.json)
console.log(user3)
const g = main()
const result = g.next()
result.value.then(data =>
const result2 = g.next(data)
if (result2.done) return
result2.value.then(data2 =>
const result3 = g.next(data2)
if (result3.done) return
result3.value.then(data3 =>
g.next(data3)
)
)
)
很明显生成器的执行器可以使用递归的方式去调用
const g = main()
function handleResult(result)
if (result.done) return
result.value.then(data =>
handleResult(g.next(data))
, err =>
g.throw(err)
)
handleResult(g.next())
生成器函数的调用其实都是差不多的,所以我们可以写一个比较通用的执行器
function co(generator)
const g = generator()
function handleResult(result)
if (result.done) return
result.value.then(data =>
handleResult(g.next(data))
, err =>
g.throw(err)
)
handleResult(g.next())
co(main)
当然这样的执行器在社区中已经有一个比较完善的库了co。这种co
的方案在2015
年之前是特别流行的,后来在出了async/await
语法糖之后,这种方案相对来讲就没有那么普及了。使用generator
这种方法最明显的变化就是异步调用回归到扁平化了
async/await
有了generator
之后js
异步编程基本上与同步代码有类似的体验了,但是使用generator
这种异步方案还需要自己手动去写一个执行器函数,会比较麻烦。在ES2017
的版本中新增了一个叫做async
的函数,它同样提供了这种扁平化的编程体验,并且是语言层面的标准的异步编程语法。其实async
函数就是生成器函数更方便的语法糖,所以语法上给generator
函数是类似的。
async function main()
try
const user1 = await ajax(/json1.json)
console.log(user1)
const user2 = await ajax(/json2.json)
console.log(user2)
const user3 = await ajax(/json3.json)
console.log(user3)
catch (error)
console.log(error)
main()
async
函数返回一个Promise
对象,更利于对整体代码控制
promise.then(() =>
console.log(all completed)
).catch(err =>
console.log(err)
)
以上是关于javascript异步编程之generator(生成器函数)与asnyc/await语法糖的主要内容,如果未能解决你的问题,请参考以下文章
JavaScript异步编程:Generator与Async