版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/j_bleach/article/details/65938474
JS异步模式
介绍$q之前,首先说一下异步。JS是一个单线程语言,一次执行一个任务,在某些情境下,需要先将某些任务“ 暂停一下(加入任务队列)“,进而去执行之后的任务,待当前任务都执行完毕时,再去响应之前“暂停的”任务,像setTimeout,ajax都是如此,这就是JS的异步模式。
$q
$q是angular中一个用来解决JS异步编程的服务,借鉴了 Kris Kowal’s Q 库,可以看作是一个轻量的Q库,遵循 Promises/A 的规范。关于Promise,各家均有实现,比如jq的Deferred(),以及ES6原生的Promise。区别是前者遵循 Promises/A的规范,后者与$q相同,遵循A 。A/A 具体区别为A的promise回调不一定是异步的。例如: JQ:
代码语言:javascript复制var log = console.log;
function MyPromise(fun){
var deferred = $.Deferred();
fun(deferred.resolve, deferred.reject);
return deferred.promise();
}
setTimeout(function () {
log(1);
});
new MyPromise(function(resolve, reject){
log(2);
resolve();
log(3);
}).then(function(){
log(4);
});
log(5);
输出顺序:23451
ES6:
代码语言:javascript复制var log = console.log;
setTimeout(function () {
log(1);
});
new Promise(function (resolve) {
log(2);
resolve();
log(3);
}).then(function () {
log(4);
});
log(5);
输出:23541
$q:
代码语言:javascript复制 function a() {
log(4);
let deferA = $q.defer();
return deferA.promise
}
function begin(promise) {
log(2);
defer.resolve();
log(3);
return promise
}
let log = console.log;
let defer = $q.defer();
let promise = defer.promise;
$timeout(function () {
log(1);
});
begin(promise).then(a)
log(5);
输出:23541
按照正常的程序执行顺序,先遇到$timeout, 由于是JS的异步函数,因此会先放在执行队列中,待后边的begin函数,与log(5)执行完毕后,再执行,所以最后输出出来。 begin函数中依次执行log(2),defer.resolve,log(3),又由于defer.resolve的异步机制会等待log(2)、log(3)、log(5)执行完毕后在执行defer.resolve中的log(4),因此最后输出23541。
$q API
通过$q.defer()方法构造出一个实例,该实例有三个方法,分别是:resolve/接受promise成功的值,reject/接受promise被拒绝,notify/再一次异步任务中,可能返回多次。在$q.defer()方法构造出的实例中,有一个promise属性,用来返回一个promise对象。
通过then方法,可以实现链式函数,来解决回调地狱的诟病。更详细的API官方都有更详实的介绍,https://docs.angularjs.org/api/ng/service/$q。下面主要介绍all,race两个方法的应用场景。
all
接受多个promise 对象,待所有promise接收完毕时(必须是resolve 的),触发then中的回掉。 在日常开发中,可能会遇到一个场景,需要发送多个请求,但请求的彼此之间没有相互依赖的关系,但需要等几个HTTP都响应完执行某个操作,这时执行某种操作。$q在这种情景下是一个不错的选择将不用的请求以数组元素的形式存放在all([promise1,promise2])中,待promise1,promise2都接受到后,会执行接下来的操作,例如:
代码语言:javascript复制 var deferredA = $q.defer();
var deferredS = $q.defer();
var _that = this;
this.searchAfter(deferredA);//进行异步操作的函数,成功后返回promise1对象
this.searchSchedules(deferredS);
$q.all([deferredA.promise, deferredS.promise])
.then(function (result) {
_that.scanArr();
_that.toDrag();
})
$q.all([]).then(function(){}) 适用于几个互相不依赖的任务执行完毕后,在调用某种方法。
race
同样接受多个promise对象,但是只要promise响应即可,因此不论是resolve,或者rejected,都可以触发race的回掉。 利用这种特性,可以做一个HTTP的超时函数,比如:
代码语言:javascript复制 var deferredA = $q.defer();
var deferredS = $q.defer();
time(deferredA){
$timeout(function(){
return deferredA.promise
},2000)
}()
http(deferredS){
$http(url,success(data,function(){
//这个不能运行哈,大致写下意思
return deferredS.promise
}))
}()
$q.race([deferredA.promise, deferredS.promise])
当2秒后,http仍然没有返回后,就可以abort掉它或者进行其他操作。因为race的机制就是只要有一个promise回来,就会触发接下来的任务,一次可以用来做超时的函数操作。
总结
在JS中,解决异步模式问题的方法还有很多,比如回掉函数,事件监听,发布订阅等等,而angular的$q提供了一个轻量的promise库,虽然方法远没有Q库的丰富,但是基础方法都有,实用性较强,在解决异步问题时,不失为一个好的选择。