一样的,这个内容感谢@MPJ老师在youtube上的funfunfuntion课程。 当年我看阮老师的generator云里雾里,实在搞不懂,为何yield出来就能够异步了?? @MPJ用了相反的过程,先给一个应用场景,再去实现异步,我算是真的搞清楚,generator异步的远离了,和你们分享~~javascript
博客和web项目更新传送门java
假设咱们有一个异步的请求,想要去经过api获取一些数据。这里借助node-fetch
库来获取数据。 fetch
能够异步的获取数据,并返回一个promise,因此常规的异步操做和写法,大体以下node
var fetch = require('node-fetch');
fetch('http://jasonplacerholder.typecoder.com/posts/1')
.then( res => res.json() )
.then( post => post.title )
.then( x => console.log('Title: ',x))
复制代码
好了,以上的代码就是一个获取api,并拿到api中的title
内容。 关于promise这里很少说,fetch返回的就是一个promise。git
那么若是使用generator会如何实现实现一样的一个异步操做呢? 这里先给结果,再来分析实现原理。这里记住co
,这个co是干吗的,一会分析并实现一个咱们本身的co函数。github
co接收一个genetor,因此咱们能够认为co就是一个generator的发动机,或者自动执行器。web
const co = require('co');
co(function *() {
const url = 'http://jasonplacerholder.typecoder.com/posts/1';
const response = yield fetch(url);
const post = yield response.json();
const title = post.title;
console.log('Title: ',title);
})
复制代码
好了,结束,执行后,会输出一样的结果,彷佛和promise没有两样。下面先简单的逐行分析,来看看在genetor中,作了什么。编程
//从genetor的第一行开始
第一行: 定义了url
第二行: 声明response,并将fetch(url)的结果.....yield
stop...
What is yield???
复制代码
嗯,因此,这个genetor的yield是干什么的?这是genetor
和普通函数的不一样之处,也是它能够作异步的基础。不一样与普通函数,genetor遇到了yield
以后,会将yield后面的处理内容抛出。json
genetor: 运行呀---运行呀---运行呀--yield? What?这是什么鬼,我搞不定,老大你帮我搞定后再加我---out..api
outer(执行器co): 收到yield返回的结果,处理----返回给genetorpromise
genetor: 收处处理结果---运行---yeild?这又是什么?你帮我搞定,out...
outer(执行器co): 收到yield返回的promise,处理---返回给genetor
这就是异步的原理了,genetor遇到yield会把任务丢出去,它就暂时不运行了。 咱们知道,yield丢出去的是一个iterator,当调用next()的时候,会返回genetor中。 因此其实co
就是一个自动触发和调度next()
的函数。
知道了原理,咱们本身来实现这个过程。而后就会比较清除整个过程了。
咱们把函数改一下
run(function *() {
const url = 'http://jasonplacerholder.typecoder.com/posts/1';
const response = yield fetch(url);
const post = yield response.json();
const title = post.title;
console.log('Title: ',title);
})
function run(generator) {
const iterator = generator(); //genetor执行会返回一个iterator,而后调用next()才会执行到下一个yield
iterator.next(); //这里打印出来的结果看一下是{value: Promise {<pending>},done:false}
}
复制代码
解释: 就如上面genetor和outer的对话,遇到yield,genetor会说:"我不知道怎么搞这个promise,你来搞吧,给你..“ 因而,外面的就会接住这个promise
咱们继续写
function run(generator) {
const iterator = generator(); //genetor执行会返回一个iterator,而后调用next()才会执行到下一个yield
const iteration = iterator.next(); //这里打印出来的结果看一下是{value: Promise {<pending>},done:false}
const promise = iteration.value;
promise.then(x => iterator.next(x)) //ok,外部帮忙处理了promise,而后处理的结果,咱们须要返回genetor,使其继续运行
//这个时候,genetor中的response拿到了值,就等于这里的x
}
复制代码
分析到这里,程序已经获得了response。 可是,下一句,立马又遇到了response.json(),一样又会丢出去一个内容,所以,咱们这里再处理一下,以下:
function run(generator) {
const iterator = generator(); //genetor执行会返回一个iterator,而后调用next()才会执行到下一个yield
const iteration = iterator.next(); //这里打印出来的结果看一下是{value: Promise {<pending>},done:false}
const promise = iteration.value;
promise.then(x => {
const anotherIterator = iterator.next(x);//注意,iterator.next()的含义,一方面会将运算结果返回,另外一方面,genetor会继续将下一个yield的任务抛出,仍然是一个iterator
const anotherPromise = anotherIterator.value;
anotherPromise.then(post => iterator.next(post))
//到此,由于iterator再也没有yield,因此不会再次返回iterator了,也不用调用next()
})
}
复制代码
至此,模拟的co
方法已经实现了。
流程以下:
yield fetch(url)
的结果,也即一个Promise。x
,调用iterator.next(x)
把x返回的同时,拿到了下一个yield
的抛出的任务。post
,并经过next(post)
返回给genetor。yield
还给大家,反正我不会,我也不会学,这任务都是大家的。也就是说,genetor的异步,就在于能将线程弹出,遇到yield
后,交出线程。因此,咱们作一个可以自动执行和触发genetor
的执行器,就能够实现异步编程,并且看起来和同步的写法很类似。 这就是库co
作的事情。
co
刚才只有两个yield
,咱们但愿方法有通用性,咱们写个递归,让它能不断的触发
function run(genetor) {
const iterator = genetor();
function autoRun(iteration) {
if(iteration.done) {return iteration.value;}
const anotherPromise = iteration.value;
anotherPromise.then(x => {
return autoRun(iterator.next(x));
})
}
return autoRun(iterator.next());
}
复制代码
好了,这样就完成了咱们本身的简易版co函数。