一樣的,這個內容感謝@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函數。