摘要:[Javascript] 使用Jscex讓Javascript程式碼更具可讀性
前言
隨著Node.js的出現,加上Windows 8也支援使用HTML和JavaScript開發桌面應用程式,
這一年來關於JavaScript的各種library和應用更是如雨後春筍般不斷的冒出,
身為一個程式開發人員面對JavaScript的機會也越來越多,
而在我們使用JavaScript進行開發的時候,最常遇到的應該是Async模型,
或是具有Callback的函數,而當在程式碼中大量使用這些具有Callback的函數時,
很容易造成程式碼趨於複雜,而導致難以閱讀和維護,
而Jscex正好可以很好的解決我們所遇到的問題。
Jscex是老趙所寫的一套Open Source Javascript Library,採用BSD授權。
實際演練
舉一個我們最常見具有Callback的Javascript Function,JQuery的$.ajax,
它的定義如下:
$.ajax({
url: 'your_api_url',
error: function(xhr) {
// Something when error occurs
},
success: function(response) {
// Something when success
}
});
假設我們今天需要使用JavaScript撰寫以下的邏輯
- 我們具有一個WebApi,可用來計算兩個int的總和
- 現在有四個變數a = 1, b = 2, c = 3, d = 4
-
變數依序兩個一組透過Api計算之後的結果,再與下一個變數繼續進行計算,直到所有變數計算完畢
(Ex. var result = a + b; result = result + c; result = result + d; ) - 最後將結果顯示在畫面上
那麼我們透過Ajax的寫法很可能就會寫成以下這樣:
$.ajax({
url: '/WebApi/CalculateService.svc',
type: 'GET',
data: { a: a, b: b },
dataType: "json",
success: function (response1) {
$.ajax({
url: '/WebApi/CalculateService.svc',
type: 'GET',
data: { a: response1.sum, b: c },
dataType: "json",
success: function (response2) {
$.ajax({
url: '/WebApi/CalculateService.svc',
type: 'GET',
data: { a: response2.sum, b: d },
dataType: "json",
success: function (response3) {
console.log($("#result"));
$("#result").text(response3.sum);
}
});
}
});
}
});
今天只有四個變數,為了在Callback Function之中繼續進行計算,就已經是三層的巢狀Function,
如果今天我有10個變數,基本上這代碼已經不具有可讀性...
如果我使用Jscex來改寫這段程式碼,它會長的像這樣
var main = eval(Jscex.compile("async", function () {
var result = 0;
result = $await(calculate(a, b));
result = $await(calculate(result, c));
result = $await(calculate(result, d));
$("#result").text(result);
}));
main().start();
是不是突然覺得
Jscex參考了C# Task Parallel Library和Asynchronous Programming,以及F# Asynchronous的優點,
來讓程式碼透過加上語義詞,以及使用Task的概念,
來讓非同步的程式碼可以以同步的方式撰寫,大大增加了程式碼的可讀性,
像在上面的程式碼中 eval(Jscex.compile("async", function) 回傳的是一個Task對象,
所以我需要在最後加上main().start()來讓Task開始運行 (其實有點像是Task Parallel Library)
而上面的程式碼其實我還將ajax的部分利用Jscex做了一些包裝,來讓程式碼使用上更方便,如下:
var Task = Jscex.Async.Task;
$.ajaxAsync = function (options) {
return Task.create(function (t) {
options.success = function (data, textStatus, jqXHR) {
t.complete("success", {
data: data,
textStatus: textStatus,
jqXHR: jqXHR
});
}
options.error = function (jqXHR, textStatus, errorThrow) {
t.complete("failure", {
jqXHR: jqXHR,
textStatus: textStatus,
errorThrow: errorThrow
});
};
$.ajax(options);
});
};
此段參考了老趙的Jscex Sample
var calculate = eval(Jscex.compile("async", function (a, b) {
var result = $await($.ajaxAsync({
url: '/WebApi/CalculateService.svc',
type: 'GET',
data: { a: a, b: b },
dataType: "json"
}));
return result.data.sum;
}));
利用Jscex將原本具有Callback的函數,改寫為同步的方式,
不但容易閱讀,也讓程式碼的維護更為輕鬆了!
在後面的文章之中,會再補充說明詳細的寫法。
效能? 安全性?
我想很多人在上面的程式碼之中,看到了一個關鍵字eval,
這是因為Jscex它會動態的compile你的JavaScript程式碼,編譯成原生的JavaScript程式碼,
在執行上面的程式時,我們可以透過瀏覽器的Console視窗看到以下資訊
// Original:
function () {
var result = 0;
result = $await(calculate(a, b));
result = $await(calculate(result, c));
result = $await(calculate(result, d));
$("#result").text(result);
}
// Jscexified:
/* async << function () { */ (function () {
var _builder_$0 = Jscex.builders["async"];
return _builder_$0.Start(this,
_builder_$0.Delay(function () {
/* var result = 0; */ var result = 0;
/* var _result_$ = $await(calculate(a, b)); */ return _builder_$0.Bind(calculate(a, b), function (_result_$) {
/* result = _result_$; */ result = _result_$;
/* var _result_$ = $await(calculate(result, c)); */ return _builder_$0.Bind(calculate(result, c), function (_result_$) {
/* result = _result_$; */ result = _result_$;
/* var _result_$ = $await(calculate(result, d)); */ return _builder_$0.Bind(calculate(result, d), function (_result_$) {
/* result = _result_$; */ result = _result_$;
/* $("#result").text(result); */ $("#result").text(result);
return _builder_$0.Normal();
});
});
});
})
);
/* } */ })
其實在瀏覽器真正eval的是Jscexified後的程式碼 (也就是下面這段)
這時候可能又會有人問說,如果讓瀏覽器動態的即時compile程式碼,是不是會造成很大的效能問題呢?
而通常上面這段程式碼,只會出現在開發環境之中,
因為在測試無誤上線之前,我們只需要透過node.js將程式碼完全編譯成非同步的程式碼即可,
執行以下command,即可無痛轉換為原生的JavaScript程式碼
node scripts/jscexc.js --input inputfile --output outputfile
結論
透過上面的例子,你感受到Jscex所帶來的魔力了嗎?
非同步的程式碼通常都是最難讀懂並維護的,也因此C#在4.0之後也在這部分花了很大的心思作改進,
而Jscex讓JavaScript的非同步程式碼不在那麼可怕,也提高了程式的可讀性,
我想這就是它的魅力所在,未來還會再陸陸續續的詳細介紹Jscex的實際使用方法,
這邊文章希望可以讓大家知道有這樣的東西存在,或許可以讓JavaScript的開發更加的平易近人,
希望對大家有幫助,也歡迎大家多多指教或一起討論 ^_^