Wind.js在移動跨平台框架PhoneGap中的異步體驗


最近正在做一個移動跨平台項目的應用開發,包括在iphone,ipad,android,windows phone等手機設備中運行混合式客戶端應用程序,這里選擇了PhoneGap的移動跨平台框架,這里我先簡單介紹下PhoneGap到底是什么東東:

 

介紹

PhoneGap是一款HTML5平台,通過它,開發商可以使用HTML、CSS及JavaScript來開發本地移動應用程序。因此,目前開發商可以只 編寫一次應用程序,然后在6個主要的移動平台和應用程序商店(app store)里進行發布,這些移動平台和應用程序商店包括:iOS、Android、BlackBerry、webOS、bada以及Symbian。

官方地址

英文官方:http://phonegap.com/

中文官方:http://www.phonegap.cn/

在上面你可以找到它的入門使用說明,這里我就不具體描述了。

關於PhoneGap開發的項目實踐經驗,我會在后面再另開文章來說明。

 

本篇文章的重點在於對於老趙的Wind.js的使用體驗,老趙是誰,相信也不用多說了,具體可以見他的博客:http://blog.zhaojie.me/

這里就先介紹他的Wind.js:

官方鏈接:http://windjs.org/

開源鏈接:https://github.com/JeffreyZhao/wind

 

對於它的定義,我就直接引用官方的一段話:

Wind.js的前身為Jscex,即JavaScript Computation EXpressions的縮寫,它為JavaScript語言提供了一個monadic擴展,能夠顯著提高一些常見場景下的編程體驗(例如異步編程)。Wind.js完全使用JavaScript編寫,能夠在任意支持JavaScript的執行引擎里使用,包括各瀏覽器及服務器端JavaScript環境(例如Node.js)。

實際上,它就是原先的Jscex。

 

對於我來說,正在開始了解Wind.js的時候,是在今年的7月份,在阿里技術嘉年華(http://adc.taobao.com/)中聽node.js專場時了解到的,當時現場火爆,大家對於Wind.js也是非常感興趣。當然過程中也有一些人保持一種觀望的態度。而對於我這種實戰派的開發者來說,在一個有應用場景的情況下,Wind.js才更有說服力。

於是,在我當時的理解當中,它應該是屬於可以改變異步體驗的一種絕佳的方式,可以讓你的代碼可讀性大大提升,而你從此不再為了setTimeout,callback之類的寫法而煩惱!

好的,一些理論的東西我就先不多說了,Wind.js文檔中已經寫的很多了,今天就來用Wind.js來體驗下如何應用到我的項目中去。

請先看下面的一段代碼:

function login(userName, password, type, callback) {
     if (callback ==  null) {
         return;
    }

    callIfNetworkAvailable( function() {
        callNativeAPI(
            native_refreshUrl,
            { key: 'Login', username: userName, password: password, type: type },
             function (result) {
                callback(result);
            }
        );
    });
}
function callIfNetworkAvailable(fn) {
     if (fn ==  null) {
         return;
    }
    getNetworkStatus( function (result) {
         if (result.status) {
             if (result.data) {
                fn();
            }
             else {
                hideLoadingMsg();
                alert(lang.networkUnAvailable);
            }
        }
         else {
            hideLoadingMsg();
            alert(lang.getNetworkAvailableStatusFailed);
        }
    });
}
function getNetworkStatus(callback) {
     if (callback ==  null) {
         return;
    }
    callNativeAPI(
        native_getUrl,
        { key: 'GetNetworkStatus' },
         function (result) {
            callback(result);
        }
    );
}
function callNativeAPI(url, data, callback) {
     var items = url.split("/");
     var serviceName = items[0];
     var actionName = items[1].toLowerCase();
     // 因為參數必須是數組,所以把參數放在一個數組中
     var params = [];
    params.push(data);
    log({ step: '調用Native接口前的參數信息', parameters: data });
     // 調用Native接口
    Cordova.exec(
         function (result) {
            log({ step: '調用Native接口的返回值信息', returnValue: result });
             if (callback !=  null) {
                callback(result);
        }
    },
     function () { },
    serviceName,
    actionName,
    params
    );
}

這里首先我先需要說的是,PhoneGap的javascript腳本與原生(iOS,android,wp等)的API的plugin交互,采用與瀏覽器webkit中的webview進行通信,而它的底層原理就是iframe的交互,它是以一種特定規范的通信協議來展開,而在傳統的web上iframe的使用本身就是最原始的異步加載原理的使用。所以,沒有辦法異步方式在phonegap的開發中廣泛使用。

 

再回過頭看上面的代碼,它實際上實現的是一個登錄的功能,在登錄的過程中,首先我必須先調用callIfNetworkAvailable方法,而它的參數本身作為一個回調函數,getNetworkStatus會調用callNativeAPI,callNativeAPI函數里面的Cordova.exec方法實際上就是一個跟原生交互的一個方法入口,通過傳遞serviceName,actionName以及對應需要傳遞的數據參數來決定,而它總是需要一個回調方法(successCallback,failCallback)來接收返回的數據。

最后的執行:

$(document).delegate("#loginPage #loginButton", "click",  function () {
     var userName = $("#username").val();
     var password = $("#password").val();
     if (isNullOrEmpty(userName)) {
        alert(lang.usernameCannotEmpty);
         return;
    }
     if (isNullOrEmpty(password)) {
        alert(lang.passwordCannotEmpty);
         return;
    }
    showLoading("登錄中...");
    login(userName, password, "normal",  function (result) {
         if (!result.status) {
            alert(result.message);
        }
         else {
            showPage("taskListPage");
            hideLoading();
        }
    });
});

 我們得到的就是需要不斷地寫嵌套函數,不斷地callback,這樣的寫法看起來還是很糾結的!

於是,萌生了采用Wind.js的異步調用的獨特方式:

先來看看我是如何改造這段代碼的:

// 用戶登錄
var loginAsync = eval(Wind.compile('async',  function (userName, password, type) {
     var result = $await(callIfNetworkAvailableAsync());
     if(result) {
         var result = $await(callNativeAPIAsync(native_refreshUrl,
        { key: 'Login', username: userName, password: password, type: type }));
         return result;
    }
     return  null;
}));

// 在當前網絡可用的情況下調用指定函數
var callIfNetworkAvailableAsync = eval(Wind.compile('async',  function() {
     var result = $await(getNetworkStatusAsync());
     if (result.status) {
         if (result.data) {
             return  true;
        }
         else {
            hideLoadingMsg();
            alert(lang.networkUnAvailable);
        }
    }
     else {
        hideLoadingMsg();
        alert(lang.getNetworkAvailableStatusFailed);
    }
     return  false;
}));

// 獲取當前網絡狀態
var getNetworkStatusAsync = eval(Wind.compile('async',  function() {
     var result = $await(callNativeAPIAsync(native_getUrl, { key: 'GetNetworkStatus' }));
     return result;
}));

// JS端與PhoneGap Native API進行交互
var callNativeAPIAsync = eval(Wind.compile('async',  function(url, data) {
     var items = url.split("/");
     var serviceName = items[0];
     var actionName = items[1].toLowerCase();
     // 因為參數必須是數組,所以把參數放在一個數組中
     var params = [];
    params.push(data);
    $await(logAsync({ step: '調用Native接口前的參數信息', parameters: data }));
     // 調用Native接口
     var result = $await($.cordovaAsync(serviceName, actionName, params));
     if(result) {
        $await(logAsync({ step: '調用Native接口的返回值信息', returnValue: result }));
    }
     return result;
}))

從代碼中,你不難發現,它采用一種順序執行代替異步的方式很巧妙地繞開了一連串callback的寫法,在感官上似乎更符合了一個開發者順序執行模型的思想。

而return result;就是一個回調的結果。

到這里你可能還有個疑問,Cordova.exec是如何做到的,這里我定義了一個叫做$.cordovaAsync的函數:

// phonegap異步調用插件
$.cordovaAsync =  function (serviceName, actionName, params) {
     return Task.create( function (t) {
         var success =  function (result) {
            t.complete("success", result);
        };
         var fail =  function(result) {
            t.complete("failure", result);
        }
        Cordova.exec(success, fail, serviceName, actionName, params);
    });
}

這里是Wind.js提供的一種任務式的插件綁定,例如它可以把一個jQuery中的$.ajax改裝成一個$.ajaxAsync的Wind.js調用方式,這里我把它改裝成$.cordovaAsync來調用Cordova.exec,而最終var result = $await($.cordovaAsync(serviceName, actionName, params));的返回值實際上就是一個回調函數的success,這樣我就實現了JS與原生客戶端的交互。

最后的執行: 

$(document).delegate("#loginPage #loginButton", "click",  function () {
     var userName = $("#username").val();
     var password = $("#password").val();
     if (isNullOrEmpty(userName)) {
        alert(lang.usernameCannotEmpty);
         return;
    }
     if (isNullOrEmpty(password)) {
        alert(lang.passwordCannotEmpty);
         return;
    }
    showLoading("登錄中...");
    startLoginAsync(userName, password, 'normal').start();
});

var startLoginAsync = eval(Wind.compile('async',  function (userName, password, type) {
     var result = $await(loginAsync(userName, password, type));
     if(result) {
         if (!result.status) {
            alert(result.message);
        }
         else {
            hideLoading();
            showPage("taskListPage");
        }
    }
}));

理解上就相當簡單了。

實例圖:

4d555398gw1dw0v5lyas1j (1).jpg

總結

會持續關注Wind.js的發展,並且接下來也會了解下它的內部原理。另外,提出一個建議,eval(Wind.compile('async',…這樣的寫法還能夠更加簡易些嗎,比如我覺得使用$.await(function(…){ … });就是挺好的方式。

開源地址:https://github.com/sunleepy/cooper-mobi


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM