2014-05-01 46 views
3

我試圖瞭解node.js v8,中的javascript生成器,而不使用任何第三方庫。如何在javascript v8生成器代碼中調用給定的回調函數?

我想嘗試具有發電機調用異步回調(類似Web請求),並返回一個被稱爲回值。 (使用節點v0.11.12)

我的代碼如下所示:

var callbackWrapper = function(){ 
    return function(callback) { 
    callback(null, 5); 
    }; 
}; 

var result = null; 
var generator = (function *(){ 
    result = yield callbackWrapper(); 
})(); 

var nextIteration = generator.next(); 

這段代碼的最後,nextIteration.value是一個函數,並結果仍然是空。

這是我的理解是,如果產量給出一個回調的方法,它會調用該回調。

我的目標是要以某種方式獲取價值,5,那個叫回來。

這樣做的方法是什麼?我是否必須通過一些函數nextIteration.value並調用它?

+2

「這是我的理解,如果yield給了一個回調方法,它會調用這個回調函數。」 編號* yield *僅返回一個值給* generator.next()*的調用者* –

回答

3

發電機不自己處理節點式回調。相反,它會返回包裝在callbackWrapper thunk中的函數。由於yield只返回一個值,然後在那個時間點暫停執行。

發電機並沒有真正設計用於控制流量,但你可以建立在它們上面創建的控制流庫如CO,暫停等。

基本上,這些庫做(我在這裏簡單化) ,是帶你的發電機功能,並遞歸調用它,直到它告訴他們它已完成。

這些庫中的每一個處理方式不同的內部收益率,例如共同把一切都可以處理成的thunk內部。雖然suspend在內部對一切使用節點式回調。

在他們檢查,看看有什麼是屈服於他們的thunk,承諾,發電機,或任何每種收益構造一個庫處理,並提取控制了基於完成當他們。

你可以圍繞發電機的結構來處理異步thunked的功能,但它不適合心臟佯攻。基本上,它會去像這樣(注:請不要使用這以外的玩弄其丟失了所有的正常檢查,錯誤處理等。):

function controlFlow(genFunc){ 
    // check to make sure we have a generator function otherwise explode 

    var generator; // reference for an initialized generator 

    // return a funcion so that execution time can be postponed 
    return function(){ 
    runGen(genFunc()); 
    } 

    function runGen(generator){ 
    var ret = generator.next(); 

    // here the generator is already finished we we'll return the final value 
    if(ret.done){ 
     return ret.value 
    } 

    // here we'll handle the yielded value no matter what type it is 
    // I'm being naive here for examples sake don't do this 
    if(typeof ret.value === 'function'){ 
     // oh look we have a regular function (very faulty logic) 
     // we wouldn't do this either but yeah 
     ret.value(function(err, result){ 
     console.log(result); 
     }); 
    } 

    // oh we got another item like an array or object that means parallelism or serialization depending on what we got back 
    // turn array, object, whatever into callback mechanisms and then track their completion 
    // we're just going to fake it here and just handle the next call 
    runGen(generator); 
    } 
} 

function thunked(callback){ 
    return function(){ 
    callback(null, 5); 
    }; 
}; 

function regular(callback){ 
    console.log('regular function call'); 
    callback(null, 'value'); 
}; 

controlFlow(function *(){ 
    yield thunked(function(err, result){ 
    console.log(err); 
    console.log(result); 
    }); 
    yield regular; 
    yield thunked(function(err, result){ 
    console.log('Another Thunked'); 
    }); 
    yield regular(function(err, result){ 
    console.log(err); 
    console.log(result); 
    }); 
})(); 
+0

實際上,發生器*是*爲控制流設計的。你的意思是說它們並不是專門用來處理異步回調的。 – Bergi

+1

生成器最初是爲了構建複雜的迭代器,因此它們是爲了控制flow.I是試圖在我的解釋過於簡單, – Thot

+2

我絕對喜歡語言結構的力量和靈活性,但它似乎是一般來說人們很難抓住他們,如果他們沒有用另一種語言與他們合作......也許是時候從其他語言挖掘一些例子並將它們轉換爲單一資源? – Thot

0

result將不會被分配,直到您通過再次調用next並將值分配給result將值發送回生成器爲止。在你的例子中,它看起來像這樣:

nextIteration.value(function (error, value) { 
    generator.next(value); 
}); 
4

讓我們澄清一些事情。

這是我的理解,如果產量給一個回調方法,它會調用該回調。

yield不調用任何東西。 yield的目的就是產生...當你定義一個生成器時,你定義一個產生值的實體。它與異步代碼無關。但是,我們可以利用生成器的重要屬性來處理異步代碼。

發電機,根據其定義,產量值。 這裏是重要的部分 - 在一個收益之間發電機是暫停。換句話說,它一直等到它被要求產生下一個值。發電機的內部狀態保存在內存中(在堆棧上),直到發電機耗盡(沒有更多的產量值)。

生成器的另一個重要屬性是我們可以發回值,從而改變它們的內部狀態。所以如果我們可以暫停發電機(讓他們等待),我們可以發回值;我們可以基本上讓他們等待一些異步操作來完成,然後將結果發送回去。

這樣做的方法是什麼?我是否必須將某個函數傳遞給nodeIteration.value並調用它?

基本上你需要用代碼包裝你的代碼。每次啓動異步操作時,都使用yield來使發生器等待。當異步操作完成時,通過將結果發送回next(result)來恢復發電機。

我寫了一篇廣泛的文章來解釋這個問題,我肯定會幫助你理解。看一看:http://eyalarubas.com/javascript-generators-and-callbacks.html

+0

EyalAr感謝博客文章 - 我一直在尋找一個解釋生成器本身的指南,而不是談論使用它們的庫。您只需將分享按鈕添加到您的博客! –

+0

@OvedD歡迎:) – EyalAr