今天我们来研究下WinJS库中的WinJS.Promise,这个东西是干什么用的呢?msdn给的翻译是承诺,不知道你们能不能理解,反正我第一次听到时是一头雾水,换一种说法,叫做“异步编程”,应该能明白吧!举个例子,你要下载一张图片,如果不用异步的话,当你开启一个线程下载时,这个线程会一载等待,直到该图片下载完成,如果使用异步编程的话,当你开启一个线程下载时,会发送一个下载图片的请求,然后继续执行该方法之后的代码,当图片开载完成后,你可以在指定的地方接收到下载完成的信息。
一:Promise
我们首先来看一个不用Promise的例子:
function withoutPromise() {
var totalSize = computedWithoutPromise();
}
function computedWithoutPromise() {
setTimeout(function () {
var count = 10000;
var totalSize = 0;
for (var i = 0; i < count; i++) {
totalSize += i;
}
return totalSize;
}, 2 * 1000);
}
一个computedWithoutPromise方法,该方法计划在2秒后执行,如果按照以上方式进行调用的话,那么你的totalSize得到的值是undefined,如果你想在computedWithoutPromise方法里面的return totalSize之后,让调用者得到结果应该怎么办呢?这时就可以使用Promise,如下代码所示:
function withPromise() {
computedWithPromise().then(function (totalSize) {
var result = totalSize;
});
}
function computedWithPromise() {
return new WinJS.Promise(function (onComplete, onError) {
setTimeout(function () {
var count = 10000;
var totalSize = 0;
for (var i = 0; i < count; i++) {
totalSize += i;
}
onComplete(totalSize);
}, 2 * 1000);
});
}
分析代码发现该写法和第一种写法有两个地方不同
1:computedWithPromise内部:在computedWithPromise方法内部,用return new WinJS.Promise(function(oncomplete,onError){});代码片断把setTimeout()给包起来了。
2:在omputedWithPromise调用的地方,不是直接将该方法写在一个变量之后,而是用.then(function(totalSize){})来进行调用。
好了,我们来分析一下,用WinJS.Promise把代码包起来,表示将该方法作为一个异步方法进行执行,.then(function(totalSize)}{)表示注册一个回调,当异步方法执行完成后,执行该方法,也就是该方法执行完成之后会以某种方式将结果返回给调用者,在本例中是computedWithPromise().then(function(totalSize))会在onComplete(totalSize)之后接收到通知,从而得到结果。也就是说在调用computedWithPromise().then(function(totalSize){})中,totalSize代表computedWithPromise返回的结果,虽然computedWithPromise是一个异步方法。
如果你把调用代码改成:
function withPromise() {
computedWithPromise().then(function (totalSize) {
var result = totalSize;
});
var a = "abc";
}
则可以看到computedWithPromise执行完后会先执行var a="abc";再执行var result=totalSize;这就是异步,这个类似于.net中的委托。
我们来重新组合一下代码,如下所示:
function withoutPromise() {
var totalSize = computedWithoutPromise();
}
function withPromise() {
computedWithPromise().then(function (totalSize) {
var result = totalSize;
},function(error){
console.log(error);
});
var a = "abc";
}
function computedWithoutPromise(onComplete) {
setTimeout(function () {
var count = 10000;
var totalSize = 0;
for (var i = 0; i < count; i++) {
totalSize += i;
}
if (onComplete) {
onComplete(totalSize);
return;
}
return totalSize;
}, 2 * 1000);
}
function computedWithPromise() {
return new WinJS.Promise(function (onComplete, onError) {
computedWithoutPromise(onComplete);
});
}
注意:
1,如果在omputedWithPromise没有调用onComplete,则在调用的地方永远不会接收到信息。
2,当异步方法执行失败时会走error的回调方法。
如果说项目中有这样一个需求:用户登录后会去服务端进行验证,验证成功后获取用户的好友列表,那么你可以这样做:
validateUser().then(function(userInfo){return getFriensList(userInfo.userID);}).then(function(friendsList){});
promise还可以这样用:
function getFriendsList(){
return new WinJS.Promise(function(onComplete,onError){
onComplete("friends list.");
})
}
function parseFriensList(){
return getFreindsList().then(function(friendsList){//getFriendsList是一个Promise
return friendsList
});
}
function getFriendsList(){
parseFriendsList().then(function(friendsList){//this frienslist has been parsed.
})
}
二,Promise.join
Promise.join是有什么用处呢?
Promise.join可以把一系列promise组合起来,等到所有promise全部完成之后再执行then后面的语句,举个例子来说明一下:
如果有这个一个需求:一个音乐播放器要下载歌曲和歌词,歌曲和歌词两个全部下载完成后会立即进行歌曲的播放并且显示歌词。
你开始的思路可能是有两个不同的promise,然后声明一个变量,在两个不同的promise完成之后对变量进行判断来确定歌曲和歌词是否全部下载完成。其实完全没必要这样做,这时你可以使用Promise.join。
下面用一个实际的例子来说明一下:
function promiseJoin() {
var downloadSongPromise = downloadSongAsync();
var downloadLyricsPromise = downloadLyricsAsync();
var promise = [downloadSongPromise, downloadLyricsPromise];
WinJS.Promise.join(promise).then(function (result) {
var a = result;
});
}
function downloadSongAsync() {
return new WinJS.Promise(function (onComplete, onError) {
setTimeout(function () {
onComplete("the song has downloaded completed.");
}, 5 * 1000);//after 5 seconds then return the result.
});
}
function downloadLyricsAsync() {
return new WinJS.Promise(function (onComplete, onError) {
setTimeout(function () {
onComplete("the lyrics has downloaded completed.");
}, 2 * 1000);
});
}
WinJS.Promise.join(promise)表示要等promise数组中的所有promise执行完成之后才执行,result是一个数组,resule[0]代表promise中的第一个promise返回的结果,result[1]代表promise中的第二个promise返回的结果,依次类推。
三,Promise.as
Promise.as有什么作用呢,是次一个非promise转换为一个Promise,应用场景举例:
接上例,在下载歌曲和歌词时,会先从缓存读取,如果缓存中没有则在从网络下载。
改造后的代码如下:
function promiseJoin() {
var downloadSongPromise = downloadSongAsync();
var downloadLyricsPromise = downloadLyricsAsync();
var promise = [downloadSongPromise, downloadLyricsPromise];
WinJS.Promise.join(promise).then(function (result) {
var a = result;
});
}
function downloadSongAsync() {
var cacheSong = readLoadData("song_cache");
if (cacheSong)
return WinJS.Promise.as(cacheSong);
else {
return new WinJS.Promise(function (onComplete, onError) {
setTimeout(function () {
onComplete("the song has downloaded completed.");
}, 5 * 1000);//after 5 seconds then return the result.
});
}
}
function downloadLyricsAsync() {
var cacheLyrics = readLoadData("lyrics_cache");
if (cacheLyrics)
return WinJS.Promise.as(cacheLyrics);
else {
return new WinJS.Promise(function (onComplete, onError) {
setTimeout(function () {
onComplete("the lyrics has downloaded completed.");
}, 2 * 1000);
});
}
}
function readLoadData(cacheKey) {
return cacheKey;
}
四,Promise.timeout
顾名思义,Promise.timeout是处理超时用的,timeout的语法是
Promise.timeout(timeout,promise).then(function(){
//execute your code
},function(error){
//execute timeout event handler
})
当你的promise的执行时间超过Promise.timieout的timeout设定的时候时,会走error回调函数。如下代码所示:
function promiseTimeout() {
var downloadSongPromise = downloadSongAsync();
var downloadLyricsPromise = downloadLyricsAsync();
var promise = [downloadSongPromise, downloadLyricsPromise];
WinJS.Promise.timeout(3 * 1000, WinJS.Promise.join(promise)).then(function (result) {
var a = result;
}, function (error) {
console.log(error);
});
}
还有一些其它的方法,像cancel,wrap等等,提供一个msdn学习网址:http://msdn.microsoft.com/zh-cn/library/windows/apps/br211867.aspx
demo下载地址:
http://yunpan.cn/QD5ZuWsvMdFcS
欢迎指正
谢谢