SharePoint 2010提供了JSOM,来让我们可以直接在页面上通过JavaScript代码与SharePoint系统直接进行交互。在Web 2.0已经日益普及的今天,JavaScript在Web开发中越来越重要,所以JSOM对于SharePoint程序员来说,是一个非常好用的利器。
SharePoint 2010 JSOM对于服务器端的请求都是采用的异步方式。如果你使用过JSOM,那么对它的异步调用方式应该很熟悉了。SP.ClientContext对象有一个executeQueryAsync()方法,JSOM都是通过调用它来完成最终的服务器端请求,这个方法有2个参数,分别是成功后的回调函数和失败后的回调函数:
var ctx = SP.ClientContext.get_current();
// ...
ctx.executeQueryAsync(function() {
// 服务器端请求成功完成
}, function() {
// 服务器端请求失败
});
其实这种回调函数的方式,在JavaScript中算得上是最常见的一种异步代码风格。但这种风格的问题是,一旦代码的逻辑需要我们将多个异步方法“串接”起来,那么我们的JavaScript代码就会变成回调套回调,无论是编写还是阅读,都变得困难起来。
现在很多JavaScript库都开始使用Promises模型来简化异步模型,包括jQuery中新增的Deferred特性,其实都是Promises模型的一种实现。Windows 8中的WinJS默认也采用了Promises风格。但Promises模型实际上并没有彻底改变对回调函数的依赖。
嗯,说了这么多,当当当,浓重介绍Jscex,Jeffery Zhao同志精心打造的用来简化JavaScript异步代码开发的一个扩展。Jscex用一种精巧的方式,在JavaScript中实现了类似.NET 4.0 TPL的异步模型。下面,我们来看看如何使用Jscex来增强SharePoint 2010 JSOM。
我选择对JSOM的扩展方式,是为SP.ClientContext添加一个方法,作为对内置executeQueryAsync()方法的替代。我们就使用executeAsync()作为新方法的名称吧。
SP.ClientContext.prototype.executeAsync = function () {
var that = this;
return Jscex.Async.Task.create(function (t) {
that.executeQueryAsync(function () {
t.complete('success');
}, function () {
t.complete('failure');
});
});
};
在为SP.ClientContext对象扩展了这个新的executeAsync()方法之后,我们就可以使用它来替代原有的executeQueryAsync()了。
我们以上一篇博客《使用JSOM创建一个SharePoint网站计数器》为例,来改造webHitCounter.js中的几个函数。
首先是getWebHitCountAsync()函数,这是这个函数之前的写法(使用了jQuery中的Deferred特性进行了封装)如下所示:
function getWebHitCountAsync() {
return $.Deferred(function (dtd) {
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var webProps = web.get_allProperties();
ctx.load(webProps);
ctx.executeQueryAsync(function () {
var hitCount = webProps.get_fieldValues()['webHitCount'];
if (!hitCount) {
hitCount = 0;
}
dtd.resolve(hitCount);
}, function () {
dtd.reject(0);
});
}).promise();
}
这是使用Jscex对其进行了改造之后的写法:
var getWebHitCountAsync = eval(Jscex.compile('async', function () {
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var webProps = web.get_allProperties();
ctx.load(webProps);
$await(ctx.executeAsync());
var hitCount = webProps.get_fieldValues()['webHitCount'];
if (!hitCount) {
hitCount = 0;
}
return hitCount;
}));
是不是清晰多了呢?函数体内,完全没有了回调函数。
接下来是increaseWebHitCountAsync()函数。由于这个函数首先需要调用getWebHitCountAsync()函数来得到当前网站计数器值,然后给这个值加一,然后再异步更新回SharePoint服务器,所以之前的代码就不得不“回调套回调”了:
function increaseWebHitCountAsync() {
return $.Deferred(function (dtd) {
getWebHitCountAsync().done(function (hitCount) {
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var webProps = web.get_allProperties();
webProps.set_item('webHitCount', ++hitCount);
web.update();
ctx.executeQueryAsync(function () {
dtd.resolve();
}, function () {
dtd.reject();
});
});
}).promise();
}
换成使用Jscex之后:
var increaseWebHitCountAsync = eval(Jscex.compile('async', function () {
var hitCount = $await(getWebHitCountAsync());
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var webProps = web.get_allProperties();
webProps.set_item('webHitCount', ++hitCount);
web.update();
$await(ctx.executeAsync());
}));
不得不说,代码比之前的版本好看多了。
我不想把本文变成一篇介绍Jscex的文章,你可以访问Jscex在github上的主页:https://github.com/JeffreyZhao/jscex,来详细了解Jscex的用法。