91在线一级黄片|91视频在线观看18|成人夜间呦呦网站|91资源欧美日韩超碰|久久最新免费精品视频一区二区三区|国产探花视频在线观看|黄片真人免费三级片毛片|国产人无码视频在线|精品成人影视无码三区|久久视频爱久久免费精品

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
使用Jscex改善JavaScript異步編程體驗(yàn)

JavaScript是互聯(lián)網(wǎng)時(shí)代編程語(yǔ)言的霸主,統(tǒng)領(lǐng)瀏覽器至今已有許多年頭,而這股風(fēng)潮很可能隨著HTML 5的興起而愈演愈烈。如今JavaScript更是在Node.js的幫助下進(jìn)軍服務(wù)器編程領(lǐng)域。“單線程”和“無(wú)阻塞”是JavaScript的天性,因此任何需要“耗時(shí)”的操作,例如等待、網(wǎng)絡(luò)通信、磁盤(pán)IO都只能提供“異步”的編程接口。盡管這對(duì)服務(wù)器的伸縮性和客戶端的響應(yīng)能力都大有脾益,但是異步接口在使用上要比傳統(tǒng)的線性編程困難許多,因此也誕生了如jQuery Deferred這樣的輔助類庫(kù)。Jscex的主要目的也是簡(jiǎn)化異步編程,但它使用了一種與傳統(tǒng)輔助類庫(kù)截然不同的方式,盡可能地將異步編程體驗(yàn)帶領(lǐng)到新的高度。

創(chuàng)新互聯(lián)主要從事成都網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)天全,十余年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來(lái)電咨詢建站服務(wù):18982081108

開(kāi)放平臺(tái)之實(shí)踐——來(lái)自百度、360、騰訊、盛大的案例分享JavaScript編程幾乎總是伴隨著異步操作,傳統(tǒng)的異步操作會(huì)在操作完成之后,使用回調(diào)函數(shù)傳回結(jié)果,而回調(diào)函數(shù)中則包含了后續(xù)的工作。這也是造成異步編程困難的主要原因:我們一直習(xí)慣于“線性”地編寫(xiě)代碼邏輯,但是大量異步操作所帶來(lái)的回調(diào)函數(shù),會(huì)把我們的算法分解地支離破碎。此時(shí)我們不能用if來(lái)實(shí)現(xiàn)邏輯分支,也不能用while/for/do來(lái)實(shí)現(xiàn)循環(huán),更不用提異步操作之間的組合、錯(cuò)誤處理以及取消操作了。

快速入門(mén):排序動(dòng)畫(huà)

我們先來(lái)看一個(gè)簡(jiǎn)單的例子?!懊芭菖判颉笔亲畛R?jiàn)的排序算法之一,它的JavaScript實(shí)現(xiàn)如下:

 
 
 
  1. var compare = function (x, y) {
  2.     return x - y;
  3. }
  4. var swap = function (array, i, j) {
  5.     var t = array[i];
  6.     array[i] = array[j];
  7.     array[j] = t;
  8. }
  9. var bubbleSort = function (array) {
  10.     for (var i = 0; i < array.length; i++) {
  11.         for (var j = 0; j < array.length - i; j++) {
  12.             if (compare(array[j], array[j + 1]) > 0) {
  13.                 swap(array, j, j + 1);
  14.             }
  15.         }
  16.     }
  17. }
 
 
 

由于某些原因——例如教學(xué)所需,我們希望能夠通過(guò)動(dòng)畫(huà)來(lái)直觀地感受不同排序算法之間的差異。將一個(gè)排序算法改寫(xiě)為動(dòng)畫(huà)效果的“基本策略”十分簡(jiǎn)單:

◆ 在每次元素“交換”和“比較”操作時(shí)暫停一小會(huì)兒(因?yàn)樗鼈兪桥判蛩惴ǖ闹饕臅r(shí)所在)。

◆ 在元素“交換”過(guò)后重繪圖像。

只需增加這樣兩個(gè)“簡(jiǎn)單”的功能,便可以形成算法的動(dòng)畫(huà)效果。但實(shí)際上,實(shí)現(xiàn)這個(gè)策略并沒(méi)有聽(tīng)上去那么容易。在其它許多語(yǔ)言或是運(yùn)行環(huán)境中,我們可以使用sleep方法來(lái)暫停當(dāng)前線程。這對(duì)代碼的邏輯結(jié)構(gòu)的影響極小。但是在JavaScript中,我們只有setTimeout可以做到“延遲”執(zhí)行某個(gè)操作。setTimeout需要與回調(diào)函數(shù)配合使用,但這會(huì)嚴(yán)重破壞算法的邏輯結(jié)構(gòu),例如,我們?cè)僖矡o(wú)法使用for來(lái)實(shí)現(xiàn)哪怕是最最簡(jiǎn)單的循環(huán)操作了。因此,排序算法的動(dòng)畫(huà)似乎只能這么寫(xiě):

 
 
 
  1. // 異步操作簽名約定:
  2. // function (arg1, arg2, ..., callback) {
  3. //     異步操作完成后使用callback回傳結(jié)果
  4. // }
  5. var compareAsync = function (x, y, callback) {
  6.     // 延遲10毫秒返回結(jié)果
  7.     setTimeout(10, function () {
  8.         callback(x - y);
  9.     });
  10. }
  11. var swapAsync = function (a, i, j, callback) {
  12.     // 交換元素
  13.     var t = a[i]; a[i] = a[j]; a[j] = t;
  14.     // 重繪
  15.     repaint(a);
  16.     // 延遲20毫秒才返回
  17.     setTimeout(20, callback);
  18. }
  19. // 外部循環(huán),從下標(biāo)為i的元素開(kāi)始處理
  20. var outerLoopAsync = function (array, i, callback) {
  21.     // 如果i還在數(shù)組長(zhǎng)度范圍內(nèi)
  22.     if (i < array.length) {
  23.         // 則進(jìn)入內(nèi)部循環(huán),與下標(biāo)為i的元素進(jìn)行比較
  24.         innerLoopAsync(array, i, 0, function () {
  25.             // 內(nèi)部循環(huán)結(jié)束以后,在外部循環(huán)中處理i的下一個(gè)元素
  26.             outerLoopAsync(array, i + 1, callback);
  27.         });
  28.     } else {
  29.         // i超出數(shù)組長(zhǎng)度,表示外層循環(huán)結(jié)束
  30.         callback();
  31.     }
  32. }
  33. // 內(nèi)部循環(huán),從下標(biāo)j開(kāi)始,與下標(biāo)為i的元素進(jìn)行比較
  34. var innerLoopAsync = function (array, i, j, callback) {
  35.     // 如果j在合適范圍內(nèi)
  36.     if (j < array.length - i) {
  37.         // 則比較下標(biāo)j及其相鄰元素
  38.         compareAsync(array[j], array[j + 1], function (r) {
  39.             // 如果次序不對(duì)
  40.             if (r > 0) {
  41.                 // 則交換及其相鄰元素
  42.                 swapAsync(array, j, j + 1, function () {
  43.                     // 交換之后,則重復(fù)內(nèi)層循環(huán)比較下標(biāo)j的下一個(gè)元a素
  44.                     innerLoopAsync(array, i, j + 1, callback);
  45.                 });
  46.             } else {
  47.                 // 假如次序已經(jīng)正確·,則直接重復(fù)內(nèi)存循環(huán)比較下標(biāo)j的下一個(gè)元a素
  48.                 innerLoopAsync(array, i, j + 1, callback);
  49.             }
  50.         });
  51.     } else {
  52.         // j已經(jīng)超出范圍,一個(gè)元素已經(jīng)處于合適的位置,內(nèi)層循環(huán)結(jié)束
  53.         callback();
  54.     }
  55. }
  56. // 冒泡排序主方法
  57. var bubbleSortAsync = function (array, callback) {
  58.     // 從***個(gè)元素開(kāi)始執(zhí)行外部循環(huán),
  59.     // 外部循環(huán)結(jié)束則意味著排序完畢
  60.     outerLoop(array, 0, callback || function () { });
  61. }
  62. // 調(diào)用
  63. var array = ...; // 初始化數(shù)組
  64. bubbleSortAsync(array);
 
 
 

相信您也可以看得出來(lái),如果使用傳統(tǒng)回調(diào)的方式來(lái)實(shí)現(xiàn)一個(gè)冒泡排序動(dòng)畫(huà)會(huì)有多么麻煩。而“支離破碎”所導(dǎo)致的更嚴(yán)重的問(wèn)題,是代碼“語(yǔ)義”方面的損失。例如,新來(lái)一位開(kāi)發(fā)人員想要維護(hù)這段代碼,他能夠看出上面這段代碼是“冒泡排序”嗎?如果您給出“冒泡排序”的動(dòng)畫(huà),又能輕易地將算法“說(shuō)明”給別人理解嗎?如果需要簡(jiǎn)單補(bǔ)充一些功能,又該將新代碼添加在何處?使用傳統(tǒng)線性編程的優(yōu)勢(shì)之一,在于容易快速編寫(xiě)出邏輯清晰而“內(nèi)聚”的實(shí)現(xiàn),即使需要補(bǔ)充一些功能,則可以通過(guò)局部變量將狀態(tài)修改控制至極小。我們幾乎可以這么說(shuō),基于回調(diào)函數(shù)的異步編程,讓許多傳統(tǒng)程序設(shè)計(jì)中總結(jié)出來(lái)的實(shí)踐與模式付諸東流。

不過(guò)有了Jscex以后世界便大不一樣了,它將編程體驗(yàn)變得“如初見(jiàn)般美好”:

 
 
 
  1. // 異步的比較操作 
  2. var compareAsync = eval(Jscex.compile("async", function (x, y) {
  3.     $await(Jscex.Async.sleep(10)); // 等待10毫秒
  4.     return x - y;
  5. }));
  6. // 異步的交換操作
  7. var swapAsync = eval(Jscex.compile("async", function (array, i, j) {
  8.     var t = array[i];
  9.     array[i] = array[j];
  10.     array[j] = t;
  11.     repaint(array); // 重繪
  12.     $await(Jscex.Async.sleep(20)); // 等待20毫秒
  13. }));
  14. // 異步的冒泡排序 
  15. var bubbleSortAsync = eval(Jscex.compile("async", function (array) {
  16.     for (var i = 0; i < array.length; i++) {
  17.         for (var j = 0; j < array.length - i; j++) {
  18.             // 執(zhí)行異步的比較操作
  19.             var r = $await(compareAsync(array[j], array[j + 1]));
  20.             if (r > 0) {
  21.                 // 執(zhí)行異步的交換操作
  22.                 $await(swapAsync(array, j, j + 1));
  23.             }
  24.         }
  25.     }
  26. }));
  27. // 調(diào)用
  28. var array = ...; // 初始化數(shù)組
  29. bubbleSortAsync(array).start();
 
 
 

以上這段代碼幾乎不用做任何解釋,因?yàn)樗耆闶窃跇?biāo)準(zhǔn)的“冒泡排序”算法之上,增加了之前所提到的“基本策略”。這便是Jscex改進(jìn)異步編程體驗(yàn)的手段:程序員編寫(xiě)最自然的代碼,并使用$await來(lái)執(zhí)行其中的異步操作。Jscex提供的編譯器(即compile方法)會(huì)將一個(gè)普通的JavaScript函數(shù)編譯為“回調(diào)函數(shù)”組織起來(lái)的異步實(shí)現(xiàn),做到“線性編碼,異步執(zhí)行”的效果。

您可以在此觀察冒泡排序的動(dòng)畫(huà)效果(需要IE9,Chrome,F(xiàn)irefox等支持Canvas的瀏覽器)。這張頁(yè)面里還實(shí)現(xiàn)了選擇排序和快速排序算法的動(dòng)畫(huà),都是基于Jscex的優(yōu)雅實(shí)現(xiàn)。如果您感興趣,也可以使用傳統(tǒng)的、基于回調(diào)的方式來(lái)編寫(xiě)這些算法動(dòng)畫(huà),然后跟頁(yè)面中的代碼實(shí)現(xiàn)進(jìn)行對(duì)比,便可以更好地了解Jscex的優(yōu)勢(shì)。

使用Jscex開(kāi)發(fā)異步程序

Jscex可以在任何支持JavaScript(ECMAScript 3)的運(yùn)行環(huán)境里執(zhí)行,例如,包括IE 6在內(nèi)的現(xiàn)代瀏覽器,服務(wù)器端的Node.js,以及如Rhino一樣的JavaScript引擎等等,它們的區(qū)別僅僅在于“引入Jscex腳本文件”的方式不同而已。Jscex模塊化十分細(xì)致,在使用時(shí)需要引入不少文件,部分原因也是由于JavaScript環(huán)境至今還缺少一個(gè)包管理機(jī)制所造成的:

◆ lib/json2.js:由Douglas Crockfod編寫(xiě)的JSON生成器,對(duì)于原生不支持JSON.stringify方法的JavaScript環(huán)境(例如早期版本的IE),則需要引入該文件。

◆ lib/uglifyjs-parser.js:UglifyJS項(xiàng)目(jQuery項(xiàng)目官方使用的壓縮工具)所使用的的JavaScript解析器,這是LISP項(xiàng)目parse-js的 JavaScript 移植,它負(fù)責(zé)Jscex中的語(yǔ)法解析工作。

◆ src/jscex.js:JIT編譯器實(shí)現(xiàn),負(fù)責(zé)在運(yùn)行時(shí)生成代碼。這也是Jscex.compile方法的具體實(shí)現(xiàn)所在。

以上三個(gè)文件構(gòu)成了Jscex的編譯器核心,它們只需在開(kāi)發(fā)環(huán)境中使用(例如在頁(yè)面引用它們),目的只是為了提供近乎原生JavaScript的開(kāi)發(fā)體驗(yàn)。對(duì)于Jscex來(lái)說(shuō),它的首要原則(沒(méi)有之一)便是“保證JavaScript程序員的傳統(tǒng)開(kāi)發(fā)體驗(yàn)”。而對(duì)于開(kāi)發(fā)和生產(chǎn)環(huán)境都必不可少的只有以下兩個(gè)文件:

◆ src/jscex.builderBase.js:Jscex中“構(gòu)造器”的公用部分。

◆ src/jscex.async.js:Jscex的“異步構(gòu)造器”,用于支持異步程序開(kāi)發(fā)。

這兩個(gè)文件在精簡(jiǎn)和gzip之后,只有3KB左右大小,幾乎不會(huì)給應(yīng)用程序帶來(lái)什么影響。

如果您要編寫(xiě)一個(gè)Jscex異步函數(shù),則只需要將一個(gè)普通的函數(shù)定義放到一段“架子”代碼中即可:

 
 
 
  1. // 普通函數(shù)
  2. var giveMeFive = function (arg0, arg1, ..., argN) {
  3.     // 實(shí)現(xiàn)
  4.     return 5;
  5. };
  6. // Jscex異步函數(shù)
  7. var giveMeFiveAsync = eval(Jscex.compile("async", function (arg0, arg1, ..., argN) {
  8.     // 實(shí)現(xiàn)
  9.     return 5;
  10. }));

Jscex.compile方法會(huì)根據(jù)它獲得的“構(gòu)造器名稱(即async)”和“函數(shù)對(duì)象”生成其對(duì)應(yīng)的“新函數(shù)”的代碼,而這段代碼會(huì)立即被eval執(zhí)行。這段“架子代碼”看上去略顯冗余,如果您覺(jué)得輸入麻煩也可以將其保存為編輯器的“代碼片段(Code Snippet)”,因?yàn)樗贘scex使用過(guò)程中幾乎不會(huì)有任何變化,我們也無(wú)需過(guò)于關(guān)注其含義。

“架子代碼”的另一個(gè)作用是“區(qū)分”普通函數(shù)和異步函數(shù)。例如上面的代碼中,giveMeFive會(huì)返回5,但giveMeFiveAsync在執(zhí)行后返回的其實(shí)是一個(gè)“將會(huì)返回5”的Future對(duì)象——在Jscex中我們將其稱為“任務(wù)”。除非我們通過(guò)start方法啟動(dòng)這個(gè)任務(wù)(Jscex異步函數(shù)中使用$await操作在需要時(shí)會(huì)調(diào)用start方法),則函數(shù)里的代碼永遠(yuǎn)不會(huì)執(zhí)行。因此,普通函數(shù)和異步函數(shù)在功能、含義和表現(xiàn)上都有不同,而通過(guò)“架子代碼”的便能很方便地把它們區(qū)分開(kāi)來(lái)。

在一個(gè)Jscex異步函數(shù)中,我們用$await操作來(lái)表示“等待任務(wù)返回結(jié)果(或出錯(cuò)),如果它還未執(zhí)行,則同時(shí)啟動(dòng)這個(gè)任務(wù)”。$await的參數(shù)是一個(gè)Jscex任務(wù)對(duì)象,我們可以把任意的異步操作輕松地封裝為一個(gè)Jscex任務(wù)。例如在Jscex的異步類庫(kù)中就內(nèi)置了Jscex.Async.sleep函數(shù),它封裝了setTimeout函數(shù)。顯然,執(zhí)行任何一個(gè)Jscex異步函數(shù),您都可以得到這樣一個(gè)標(biāo)準(zhǔn)的異步任務(wù)對(duì)象。

除了在Jscex異步函數(shù)中通過(guò)$await來(lái)操作之外,我們也可以手動(dòng)調(diào)用任務(wù)的start方法來(lái)啟動(dòng)一個(gè)任務(wù)。Jscex異步任務(wù)模型雖然簡(jiǎn)單,但它是Jscex異步編程的基石,它讓“編譯器”的核心功能變得小巧、簡(jiǎn)單和緊湊,許多功能以及使用模式都能在“類庫(kù)”層面擴(kuò)展出來(lái)。在今后的文章中,我們也會(huì)了解如何將一個(gè)異步操作封裝為Jscex任務(wù),以及圍繞這個(gè)任務(wù)模型進(jìn)行開(kāi)發(fā)和擴(kuò)展。

平易近人的編譯器和eval

從我之前的經(jīng)驗(yàn)來(lái)看,一些朋友可能會(huì)被“編譯器”的字樣嚇到,認(rèn)為Jscex是一個(gè)“重型”的解決方案。還有一些朋友在腦海里深深印有“eval很邪惡”的印象,于是同樣望而卻步。其實(shí)這些都是對(duì)Jscex的誤解,這里我打算著重解釋一下這方面的問(wèn)題。

如今“編譯器”其實(shí)并不是什么特別神秘的東西,事實(shí)上可能您早就在使用針對(duì)JavaScript的編譯器了。例如,Google的Closure Compiler便是這樣一個(gè)東西。Closure Compiler會(huì)接受一段JavaScript代碼,并輸出其“等價(jià)”并“精簡(jiǎn)”后的代碼。Closure Compiler的作用是“減小文件體積”,而Jscex的作用便是將一個(gè)JavaScript函數(shù)轉(zhuǎn)化成一個(gè)新的函數(shù),以符合某些場(chǎng)景(如異步編程)的需要而已。另一方面,Jscex的轉(zhuǎn)換操作也涉及代碼解析,語(yǔ)法樹(shù)的優(yōu)化以及新代碼的輸出,因此無(wú)論從功能還是從實(shí)現(xiàn)角度來(lái)說(shuō),Jscex的核心都是一個(gè)標(biāo)準(zhǔn)的“編譯器”。

傳統(tǒng)的編譯器往往會(huì)給開(kāi)發(fā)人員在代碼執(zhí)行之前增加一個(gè)額外步驟(編譯),這對(duì)編程體驗(yàn)是一種損害。JavaScript程序員往往習(xí)慣于“修改后刷新頁(yè)面”便能立即看到結(jié)果,但是如某些將C#或Java語(yǔ)言轉(zhuǎn)化為JavaScript的解決方案,往往都需要開(kāi)發(fā)人員在“刷新頁(yè)面”之前重新生成一遍JavaScript代碼。Jscex則不然,正如之前提到的那樣,Jscex的首要原則是“盡可能保證JavaScript程序員的傳統(tǒng)開(kāi)發(fā)體驗(yàn)”。Jscex編譯器的一大特色,便是“在運(yùn)行時(shí)生成代碼”。Jscex只是JavaScript開(kāi)發(fā)中所使用的類庫(kù),它幾乎不會(huì)對(duì)“JavaScript編程”本身有任何改變。換句話說(shuō),開(kāi)發(fā)人員編寫(xiě)的就是JavaScript代碼,它的載體就是普通的JavaScript文件,文件加載也好,代碼執(zhí)行行為也罷,都和普通的JavaScript開(kāi)發(fā)一樣。當(dāng)您修改了Jscex異步函數(shù)的實(shí)現(xiàn)之后,Jscex.compile方法在代碼執(zhí)行時(shí)自然會(huì)生成新的函數(shù)代碼,因此并不會(huì)給開(kāi)發(fā)人員增加任何額外負(fù)擔(dān)。

Jscex.compile生成的代碼會(huì)由eval執(zhí)行,有朋友會(huì)認(rèn)為這么做會(huì)影響性能或是安全性。但事實(shí)上,無(wú)論是eval還是Jscex.compile,都只是為了保證開(kāi)發(fā)過(guò)程中的體驗(yàn)(修改后立即生效)。真正在生產(chǎn)環(huán)境里執(zhí)行的代碼,是不會(huì)出現(xiàn)eval和Jscex.compile的,因?yàn)镴scex還提供了一個(gè)AOT編譯器(相對(duì)于在運(yùn)行時(shí)生成代碼的JIT編譯器而言)。

AOT編譯器也是一段JavaScript代碼,使用Node.js執(zhí)行。使用方法為:

 
 
 
  1. node scripts/jscexc.js --input input_file --output output_file

AOT編譯器會(huì)靜態(tài)分析輸入的腳本文件,找出其中的eval與Jscex.compile函數(shù)調(diào)用,直接將“動(dòng)態(tài)編譯”的結(jié)果寫(xiě)入eval處。例如compareAsync的原始代碼:

 
 
 
  1. var compareAsync = eval(Jscex.compile("async", function (x, y) {
  2.     $await(Jscex.Async.sleep(10)); 
  3.     return x - y;
  4. })); 

編譯后的代碼便會(huì)成為如下形式,目前您無(wú)需理解這段代碼的含義。Jscex對(duì)最終編譯輸出的代碼經(jīng)過(guò)精心設(shè)計(jì),盡可能地讓其保留可讀性及可調(diào)式性,這點(diǎn)在今后的文章中也會(huì)加以說(shuō)明和演示。

 
 
 
  1. var compareAsync = (function (x, y) {
  2.     var $_b = Jscex.builders["async"];
  3.     return $_b.Start(this,
  4.         $_b.Delay(function () {
  5.             return $_b.Bind(Jscex.Async.sleep(10), function () {
  6.                 return $_b.Return(x - y);
  7.             });
  8.         })
  9.     );
  10. });

原始代碼在經(jīng)過(guò)AOT編譯之后,不僅在運(yùn)行性能方面有所提高(節(jié)省了編譯和動(dòng)態(tài)執(zhí)行的開(kāi)銷,并可以在ECMAScript 5的Strict Mode下運(yùn)行),還能讓代碼擺脫Jscex編譯器執(zhí)行。在排除了編譯器代碼之后,Jscex的異步類庫(kù)在精簡(jiǎn)和壓縮后只有3KB左右大小,十分適合互聯(lián)網(wǎng)產(chǎn)品使用。

總結(jié)

異步編程的困難有目共睹,因此為了輔助異步程序開(kāi)發(fā)出現(xiàn)過(guò)許多嘗試。在JavaScript編程領(lǐng)域,大部分解決方案都是設(shè)法通過(guò)提供一些API來(lái)更好地組織錯(cuò)綜復(fù)雜的回調(diào)函數(shù),但Jscex走的是另外一條道路。Jscex的目的,是希望盡可能保留JavaScript程序員原有的編程習(xí)慣及邏輯組織方式,讓“編譯器”來(lái)生成那些包含了回調(diào)函數(shù)的代碼。類似的功能已經(jīng)在F#和Scala中獲得了成功,也即將在下個(gè)版本的C#里出現(xiàn),而Jscex則是將其引入至JavaScript編程中。

Jscex基于BSD授權(quán)協(xié)議開(kāi)源,代碼庫(kù)放置在GitHub上,并同步至SNDA Code。

原文:http://www.infoq.com/cn/articles/jscex-javascript-asynchronous-programming


當(dāng)前名稱:使用Jscex改善JavaScript異步編程體驗(yàn)
文章URL:http://m.jiaoqi3.com/article/dppdsgh.html