Generator 异步计划
相比于传统回调函数的形式解决异步调用,Promise
最大的劣势就是能够链式调用解决回调嵌套的问题。然而这样写仍然会有大量的回调函数,尽管他们之间没有嵌套,然而还是没有达到传统同步代码的可读性。如果以上面的形式写异步代码,它是很简洁,也更容易浏览的。
// like sync modetry{ 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)})
原文地址: https://kspf.xyz/archives/21
更多内容微信公众号搜寻充饥的泡饭
小程序搜一搜开水泡饭的博客