JavaScript Promise 用法
同步编程通常来说易于调试和维护,然而,异步编程通常能获得更好的性能和更大的灵活性。异步的最大特点是无需等待。“Promises”渐渐成为JavaScript里最重要的一部分,大量的新API都开始promise原理实现。下面让我们看一下什么是promise,以及它的API和用法!
Promises现状
XMLHttpRequestAPI是异步的,但它没有使用promiseAPI。但有很多原生的javascriptAPI使用了promise:
*BatteryAPI
*fetchAPI(XHR的替代品)
*ServiceWorkerAPI
Promises将来只会变得越来越流行、普遍,非常重要,所有的前端开发人员都将用到它。另一个值得注意的是,Node.js是基于Promises的平台(很显然,Promise是它的一个核心特征)。
Promises的用法比你想象的要简单——如果你以前喜欢使用setTimeout来控制异步任务的话!
Promise基本用法
newPromise()构造器可以用在传统的异步任务中,就像以前setTimeout和XMLHttpRequest的用法一样。一个新的Promise使用new关键字生成,同时,这个Promises提供了resolve和reject函数让我们执行回调操作:
varp=newPromise(function(resolve,reject){
//Doanasynctaskasynctaskandthen...
if(/*goodcondition*/){
resolve('Success!');
}
else{
reject('Failure!');
}
});
p.then(function(){
/*dosomethingwiththeresult*/
}).catch(function(){
/*error*/
})
程序员可以手动的在回调函数内部根据执行情况调用resolve和reject函数。下面是一个比较具有现实意义的例子,它将一个XMLHttpRequest调用转换为基于Promises的任务:
//FromJakeArchibald'sPromisesandBack:
//http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest
functionget(url){
//Returnanewpromise.
returnnewPromise(function(resolve,reject){
//DotheusualXHRstuff
varreq=newXMLHttpRequest();
req.open('GET',url);
req.onload=function(){
//Thisiscalledevenon404etc
//socheckthestatus
if(req.status==200){
//Resolvethepromisewiththeresponsetext
resolve(req.response);
}
else{
//Otherwiserejectwiththestatustext
//whichwillhopefullybeameaningfulerror
reject(Error(req.statusText));
}
};
//Handlenetworkerrors
req.onerror=function(){
reject(Error("NetworkError"));
};
//Maketherequest
req.send();
});
}
//Useit!
get('story.json').then(function(response){
console.log("Success!",response);
},function(error){
console.error("Failed!",error);
});
Promise.resolve()和Promise.reject()可以直接被调用。有时候,当判断出promise并不需要真正执行时,我们并不需要使用new创建Promise对象,而是可以直接调用Promise.resolve()和Promise.reject()。比如:
varuserCache={};
functiongetUserDetail(username){
//Inbothcases,cachedornot,apromisewillbereturned
if(userCache[username]){
//Returnapromisewithoutthe"new"keyword
returnPromise.resolve(userCache[username]);
}
//UsethefetchAPItogettheinformation
//fetchreturnsapromise
returnfetch('users/'+username+'.json')
.then(function(result){
userCache[username]=result;
returnresult;
})
.catch(function(){
thrownewError('Couldnotfinduser:'+username);
});
}
因为promise肯定会返回,所以,我们可以使用then和catch方法处理返回值!
所有的promise对象实例里都有一个then方法,它是用来跟这个promise进行交互的。首先,then方法会缺省调用resolve()函数:
newPromise(function(resolve,reject){
//AmockasyncactionusingsetTimeout
setTimeout(function(){resolve(10);},3000);
})
.then(function(result){
console.log(result);
});
//Fromtheconsole:
//10
then回调动作的触发时机是promise被执行完。我们还可以串联then方法执行回调操作:
newPromise(function(resolve,reject){
//AmockasyncactionusingsetTimeout
setTimeout(function(){resolve(10);},3000);
})
.then(function(num){console.log('firstthen:',num);returnnum*2;})
.then(function(num){console.log('secondthen:',num);returnnum*2;})
.then(function(num){console.log('lastthen:',num);});
//Fromtheconsole:
//firstthen:10
//secondthen:20
//lastthen:40
你会发现,每次then调用都会以之前的then调用的返回值为参数。
如果一个promise已经执行完成,单then被再次调用时,回调动作将会被再次执行。而如果这个promise里执行的是reject回调函数,这是再调用then方法,回调函数将不会被执行。
catch方法
catch当一个promise被拒绝(reject)时,catch方法会被执行:
newPromise(function(resolve,reject){
//AmockasyncactionusingsetTimeout
setTimeout(function(){reject('Done!');},3000);
})
.then(function(e){console.log('done',e);})
.catch(function(e){console.log('catch:',e);});
//Fromtheconsole:
//'catch:Done!'
通常我们在reject方法里处理执行失败的结果,而在catch里执行异常结果:
reject(Error('Datacouldnotbefound'));
Promise.all方法
在我们的异步调用时经常有这样一种场景:我们需要同时调用多个异步操作,但希望只有等所有的操作都完成后,我们才去执行响应操作——这就是Promise.all的作用。Promise.all方法可以接收多个promise作为参数,以数组的形式,当这些promise都成功执行完成后才调用回调函数。
Promise.all([promise1,promise2]).then(function(results){
//Bothpromisesresolved
})
.catch(function(error){
//Oneormorepromiseswasrejected
});
一个很好的能演示Promise.all用法的例子是,执行多个AJAX操作(通过fetch)调用:
varrequest1=fetch('/users.json');
varrequest2=fetch('/articles.json');
Promise.all([request1,request2]).then(function(results){
//Bothpromisesdone!
});
我们还可将fetch和电池状态API混合一起执行,因为它们返回的都是promise:
Promise.all([fetch('/users.json'),navigator.getBattery()]).then(function(results){
//Bothpromisesdone!
});
一旦promise里调用了reject函数,也就是执行被拒绝了,没有能够正常完成,情况会有些复杂。一旦promise被拒绝,catch方法会捕捉到首个被执行的reject函数:
varreq1=newPromise(function(resolve,reject){
//AmockasyncactionusingsetTimeout
setTimeout(function(){resolve('First!');},4000);
});
varreq2=newPromise(function(resolve,reject){
//AmockasyncactionusingsetTimeout
setTimeout(function(){reject('Second!');},3000);
});
Promise.all([req1,req2]).then(function(results){
console.log('Then:',one);
}).catch(function(err){
console.log('Catch:',err);
});
//Fromtheconsole:
//Catch:Second!
Promise.all是非常重要的接口,将会在很多新诞生的promiseAPI中扮演重要的作用。
Promise.race
Promise.race是一个有趣的函数——它不是等待所有的promise被resolve或reject,而是在所有的promise中只要有一个执行结束,它就会触发:
varreq1=newPromise(function(resolve,reject){
//AmockasyncactionusingsetTimeout
setTimeout(function(){resolve('First!');},8000);
});
varreq2=newPromise(function(resolve,reject){
//AmockasyncactionusingsetTimeout
setTimeout(function(){resolve('Second!');},3000);
});
Promise.race([req1,req2]).then(function(one){
console.log('Then:',one);
}).catch(function(one,two){
console.log('Catch:',one);
});
//Fromtheconsole:
//Then:Second!
一个有用的场景是,从多个镜像服务器下载资源,一旦有一个返回,其它的返回也就不用处理了。
学会使用Promises
Promises在过去几年是一个非常火爆的话题,它甚至从JavaScript里抽离出来变成了一个语言架构。相信很快我们将见到有愈来愈多的JavaScriptAPI将使用以promise为基础的模式。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。