好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Wind.js在移动跨平台框架PhoneGap中的异步体验

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");
        }
    }
}));

理解上就相当简单了。

实例图:

总结

会持续关注Wind.js的发展,并且接下来也会了解下它的内部原理。另外,提出一个建议,eval(Wind.compile('async',…这样的写法还能够更加简易些吗,比如我觉得使用$.await(function(…){ … });就是挺好的方式。

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

作者: Leepy

 

邮箱:sunleepy(AT)gmail.com

 

出处: http://liping13599168.cnblogs.com/  

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。

 

 

分类:  PhoneGap ,  Windjs

标签:  windjs ,  phonegap ,  移动跨平台

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于Wind.js在移动跨平台框架PhoneGap中的异步体验的详细内容...

  阅读:49次