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 =https://www.huyubaike.com/biancheng/> {const result2 = g.next(data)if (result2.done) returnresult2.value.then(data2 => {const result3 = g.next(data2)if (result3.done) returnresult3.value.then(data3 => {g.next(data3)})})})
很明显生成器的执行器可以使用递归的方式去调用
const g = main()function handleResult(result) {if (result.done) returnresult.value.then(data =https://www.huyubaike.com/biancheng/> {handleResult(g.next(data))}, err => {g.throw(err)})}handleResult(g.next())
生成器函数的调用其实都是差不多的,所以我们可以写一个比较通用的执行器
function co(generator) {const g = generator()function handleResult(result) {if (result.done) returnresult.value.then(data =https://www.huyubaike.com/biancheng/> {handleResult(g.next(data))}, err => {g.throw(err)})}handleResult(g.next())}co(main)
经验总结扩展阅读
- 2022年12月30日腊八节开业好吗
- 为什么很少看见男生喝奶茶 经常喝奶茶对身体好吗
- 2023年大暑出生的男孩名字 大暑生的男孩取什么名字比较好
- 大暑出生的男孩名 必成大器的男孩名字
- 2023年大暑出生宝宝取名 2023年大暑出生宝宝名字
- 兔年大暑出生的女孩名字 大暑出生的女孩取名
- 未来一个月运势大旺得福星庇佑 迟早翻身享受生活的3大星座
- 夏至当天吃什么
- 本周开始四大星座转运发财 贵胄也多幸福的生活就在眼前
- 女生学什么专业好就业 哪些专业有前途