惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
Google Developers Blog
Jina AI
Jina AI
大猫的无限游戏
大猫的无限游戏
Martin Fowler
Martin Fowler
博客园 - 司徒正美
云风的 BLOG
云风的 BLOG
C
Cybersecurity and Infrastructure Security Agency CISA
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
S
Securelist
S
Security Affairs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
L
LINUX DO - 热门话题
博客园 - 三生石上(FineUI控件)
T
Threatpost
T
The Blog of Author Tim Ferriss
C
CERT Recently Published Vulnerability Notes
IT之家
IT之家
P
Palo Alto Networks Blog
Microsoft Azure Blog
Microsoft Azure Blog
Spread Privacy
Spread Privacy
Cyberwarzone
Cyberwarzone
腾讯CDC
L
LangChain Blog
Know Your Adversary
Know Your Adversary
C
CXSECURITY Database RSS Feed - CXSecurity.com
GbyAI
GbyAI
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
I
Intezer
T
Tor Project blog
AWS News Blog
AWS News Blog
T
Tenable Blog
NISL@THU
NISL@THU
Security Latest
Security Latest
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
H
Hackread – Cybersecurity News, Data Breaches, AI and More
人人都是产品经理
人人都是产品经理
MongoDB | Blog
MongoDB | Blog
MyScale Blog
MyScale Blog
D
DataBreaches.Net
Microsoft Security Blog
Microsoft Security Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
量子位
美团技术团队
The Cloudflare Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
罗磊的独立博客
The GitHub Blog
The GitHub Blog
阮一峰的网络日志
阮一峰的网络日志
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Stack Overflow Blog
Stack Overflow Blog

博客园 - Leepy

AI应用平台搭建之旅(上) - 框架篇(附:AICon大会阿里国际Agent应用平台分享) 阿里巴巴LangEngine开源了!支撑亿级网关规模的高可用Java原生AI应用开发框架 在mac上安装Go语言初体验 iOS学习系列 - 标签Tag列表的实现 iOS学习系列 - UITableView下拉更新/上提加载的实现 iOS学习系列 - MonoTouch绑定原生Obj-C静态库的实现 iOS学习系列 - 在iOS客户端实现google oauth2登录以及在asp.net服务端上form认证 iOS学习系列 - 利用ASIHTTPRequest实现异步队列 iOS学习系列 - 扩展机制category与associative Mono for Andriod学习与实践(1)— 初体验 阿里技术嘉年华官网上线啦! Silverlight实现对Sql Server Profiler的SQL实时监控 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习) Silverlight 4以下版本模拟鼠标双击事件 利用Nginx+Mono+Fastcgi代替IIS对Asp.Net进行反向代理 Thrift初探:简单实现C#通讯服务程序 对于.Net中C#指针的研究 各大主流.Net的IOC框架性能测试比较 依赖注入框架Autofac的简单使用
Wind.js在移动跨平台框架PhoneGap中的异步体验
Leepy · 2012-09-02 · via 博客园 - Leepy

最近正在做一个移动跨平台项目的应用开发,包括在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