3

我正在尋找一種添加到Javascript的非搶先式多線程非常具體形式的方法。 Mozilla的Javascript 1.7支持使用yield的本地協程,但我不喜歡使用瀏覽器特定的解決方案。我看到有幾個continuation或coroutines的實現,它們是基於將帶註釋的Javascript代碼轉換爲純Javascript。一些示例是StratifiedJS,Narrative Javascriptjwacs生成Javascript代碼的延續後面的技巧是什麼?

我不需要模擬Javascript異步調用的全功能框架;我只是需要它的一個非常具體的用法,我想實現。因此,上面的庫對我來說是一個矯枉過正。

有人能告訴我這種預處理器使用的基本「技巧」(或技巧)嗎?是否有一些特殊的語言黑客可以在Javascript中實現延續,但需要花費額外的代碼?歡迎任何相關參考。

回答

10

這是延續傳球風格

JavaScript是Lisp的,但穿的語法C.

,因爲JavaScript是其核心函數式語言的衣服,很瘋狂的招數是可能的,就像路過延續風格。但這些技巧令人頭痛。

總的來說,延續是下一步做什麼的概念 - 作爲你可以調用的東西,就像一個函數一樣。我有時也會將延續視爲已保存的調用幀堆棧:您可以將一堆函數調用保存爲執行狀態,並稍後返回或僅「調用」此狀態。

有人表示,通過將代碼轉換爲連續傳遞樣式,您可以獲得延續的力量。哇!這真的很令人印象深刻:

只是一個源代碼轉換和whooosh你有延續的力量。

現在,Javascript的問題是它的C語法。使用C語法進行源代碼轉換是很困難的。使用Lisp語法會更容易,但仍然乏味且容易出錯。

我們很幸運,一些非常聰明的人爲我們做了艱苦的工作。這項艱鉅的工作需要使用JavaScript解析器,因爲這種轉換真的意味着什麼?在一個總結中,它意味着重新排序操作的順序,以便首先真正完成的任務先行。

f(g(a + x)) 

添加a + x首先做了,那麼函數調用g()然後f()。有三個子表達式。在CPS轉換中,子表達式的結果被傳遞給繼續。這涉及到創建許多內部幫助函數作爲臨時延續。這會變得複雜而乏味,我們將在下面看到。

http://en.wikipedia.org/wiki/Continuation-passing_style示例功能

(define (pyth x y) 
    (sqrt (+ (* x x) (* y y)))) 

轉換爲

(define (pyth& x y k) 
    (*& x x (lambda (x2) 
     (*& y y (lambda (y2) 
       (+& x2 y2 (lambda (x2py2) 
          (sqrt& x2py2 k)))))))) 

這對應於Javacript

function pyth(x, y) { 
    return Math.sqrt(x * x + y * y); 
} 

但*,+和Math.sqrt()不是函數CPS會有意義。

但讓我們假設*,+和Math.sqrt()是Web服務的例子。這很重要,因爲Javascript Web服務調用是異步。每個使用異步調用的人都知道如何將它們的結果組合起來。使用預處理庫或生成器可以更輕鬆地處理異步結果。

讓我們寫在一個不同的方式例如:

function pyth(x, y) { 
    return sqrt(add(mul(x, x), mul(y, y))); 
} 

那麼CPS變換看起來是這樣的:

function pyth_cps(x, y, k) { 
    mul_cps(x, x, function(x2) { 
    mul_cps(y, y, function(y2) { 
     add_cps(x2, y2, function(x2py2) { 
     sqrt_cps(x2py2, k); 
     }) 
    }) 
    }); 
} 

我們看到產生的代碼被撕裂由內向外,積極提出不可讀。每個功能都被轉換。他們都得到了一個神奇的參數k。這是延續。在JavaScript中,它是一個獲取操作結果的函數。在調用堆棧k內部的某處調用。在我們的例子中,在這裏沒有顯示sqrt()的CPS轉換。

另請注意,CPS轉換函數不會返回。他們只是將計算結果稱爲延續。這可能導致堆棧耗盡。所有Javascript CPS變形金剛需要處理這個。在Scheme中,這不是必需的,因爲所有的調用都是尾調用。尾部調用不需要額外的調用框架。在JavaScript中需要使用蹦牀或類似技術。不是直接調用continuation,而是調用helper並將結果和延續傳遞給它。助手以無限循環運行,始終調用並返回並避免堆棧耗盡。

那麼,爲什麼這個CPS給我們延續的力量呢?那是因爲接下來就是接下來要做的事。如果我們總是將這個概念作爲一個附加參數k來傳遞,並且總是將當前表達式的結果傳遞給它,那麼我們已經在代碼中實現了這個概念。但是,正如我們所看到的,這個「總是隨身攜帶」是很乏味的。

即使我們讓源代碼預處理器做了艱苦的工作,這也是一個陡峭的代價。我們爲什麼要使用延續?抽象控制流是可能的。 Seaside是一個Web應用程序框架,它使用continuation來抽象瀏覽器的無狀態請求流。用戶交互可以簡潔地建模 - 人們不再在請求中思考,而是在交互流中進行思考。這只是延續力量的衆多例子之一。這種力量對許多人來說似乎也很古怪,有點可怕。

+0

我很瞭解CPS,但是轉換通用命令代碼a-la-javascript並不那麼簡單。你如何從順序代碼自動轉換爲CPS? – 2012-02-22 11:42:20

+0

首先解析源代碼,然後重新排列表達式,以便評估第一個子表達式並將表達式的結果傳遞給繼續。 I. e。按照此處所述執行:http://en.wikipedia.org/wiki/Continuation-passing_style,但使用Javascript。 – nalply 2012-02-22 12:45:04