相比于传统回调的方式,Promise的去处理异步调用的优势在于链式调用解决回调嵌套过深的问题
但是大量的异步函数导致的可读性差的问题并没有得以解决
我们可以使用ES2015提供的Generator(生成器函数)
Generate的基本使用
- 首先生成器函数是在普通函数的基础上多了个*号
- 我们在调用生成器函数时并不会直接去执行这个函数,而是得到一个生成器实例对象
- 知道我们去手动调用生成器对象的next方法,生成器函数的函数体才会去执行
- 另外,我们可以在生成器内部可以随时使用yield关键词去返回一个值
- 我们可以在next返回对象当中的value里拿到这个值,这个返回的对象中还会有一个done属性来代表该生成器是否全部执行完毕
- 要注意的是yield关键词与return语句不同,它只是暂时暂停生成器函数执行,而不是立即结束这个函数的执行
- 当调用下一次生成器next方法时,生成器就会继续往下执行
- 如果在next方法中我们传入一个值,那么这个值会作为yield语句的返回值,那么我们就可以声明一个对象来接收到这个传入的值
- 此外我们如果使用生成器的throw方法可以让生成器内部抛出一个异常
- 那么这个生成器内部继续往下执行的时候我们就可以得到这个异常
这里我们使用try catch去捕获这个异常 ```json function * foo (){ console.log(‘start’)
try { const res = yield ‘foo’ // 我们可以在生成器内部可以随时使用yield关键词去返回一个值 console.log(res) } catch (e){ console.log(e) }
// yield关键词与return语句不同,他只是暂时暂停生成器函数执行 // 当调用下一次生成器next方法时,生成器就会继续往下执行 // 如果在next方法中我们传入一个值,我们可以在yield中得到这个传入值的 }
const generator = foo() // 此时并没有调用生成器函数,而是取得一个生成器对象
const result = generator.next() // 通过调用生成器对象的next方法,函数体才会开始执行,直到执行到yield关键词所在的位置才会停止,并把yield后面的值返回出去,这时函数才会暂停下来
console.log(result) // 返回的值中还会有一个done属性来代表该生成器是否全部执行完毕 // generator.next()
generator.throw(new Error(‘Generator error’)) // 使用生成器的throw方法可以往生成器内部抛出一个异常 // 在这种情况下继续往下执行的化,我们就可以得到这个异常
<a name="3VD8O"></a>
## Generator异步方案
1. 首先我们先定义一个main的生成器函数
2. 然后在这个函数内部我们使用yield返回一个ajax调用,也就是返回了一个Promise对象,接着用声明一个users来接收这个返回值
3. 在外部我们就可以调用这个生成器函数来得到一个生成器对象实例g
4. 调用生成器对象的next方法,那么我们的main函数就会执行到第一个yield的位置,也就是会执行第一个ajax的调用
5. 此时next方法返回对象的value就是yield返回的Promise的对象,因此我们可以使用then方法去指定该Promise的回调,那么我们就可以在这个Promise的回调当中拿到Promise的执行结果
6. 我们可以通过再一次调用next把我们得到的这个data传递进去,那么我们的main函数就可以继续往下执行了
7. 而且我们传递进去的data会作为我们当前这个yield的返回值,就可以顺利拿到result
8. 以此类推,我们可以使用递归的方式不断迭代,直到返回的done属性为true
```json
function ajax (url){
return new Promise (function(resolve,reject){
var xhr = new XMLHttpRequest()
xhr.open('GET',url)
xhr.responseType = 'json'
xhr.onload = function(){
if (this.status == 200){
resolve(this.response)
}else{
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
function * main () {
const users = yield ajax('/users.json')
console.log("users",users)
const posts = yield ajax('/posts.json')
console.log("posts",posts)
}
const g = main()
const result = g.next()
result.value.then(data=>{
const result3 = g.next(data)
if(result3.done) return
result3.value.then(data=>{
g.next(data)
})
})