一、$q
$q是Angular的一种内置服务,可以使你异步地执行函数,并且当函数执行完成时它允许你使用函数的返回值(或异常)。
总结
方法 | 作用 | 适用场景 |
---|---|---|
$q.defer() | 创建 deferred 对象(老式) |
不推荐,用 $q.when() 代替 |
$q.when(value) | 转换普通值/已有 Promise | 适用于 同步或异步返回值 |
$q.reject(reason) | 创建一个失败的 Promise | 适用于 错误处理 |
$q.all([p1, p2, ...]) | 等 所有 Promise 完成后返回 结果 | 适用于 多个并行任务 |
$q.race([p1, p2, ...]) | 谁先完成就返回谁 | 适用于 多个竞争任务 |
$q.all([...])
介绍
$q.all([...])
是 AngularJS 提供的 Promise 组合工具,用于 并行执行多个异步任务,并在 所有任务都完成后 统一返回结果。它的行为类似于 JavaScript 原生的 Promise.all([...])
。
核心特点
- 同时执行多个异步任务(
$http
请求、定时任务、数据库查询等)。 - 等所有任务完成后,再执行
then()
逻辑。 - 如果其中任何一个 Promise 失败,整个
$q.all()
都会被reject
。 - 返回的
then()
处理的是一个 结果数组,每个元素对应一个 Promise 的结果。
$q.all([...])
使用示例
示例 1:基本用法
angular.module("myApp", [])
.controller("MainController", function ($scope, $q, $timeout) {
function asyncTask1() {
var deferred = $q.defer();
$timeout(function () {
console.log("任务 1 完成");
deferred.resolve("数据 1");
}, 2000);
return deferred.promise;
}
function asyncTask2() {
var deferred = $q.defer();
$timeout(function () {
console.log("任务 2 完成");
deferred.resolve("数据 2");
}, 1000);
return deferred.promise;
}
function asyncTask3() {
var deferred = $q.defer();
$timeout(function () {
console.log("任务 3 完成");
deferred.resolve("数据 3");
}, 1500);
return deferred.promise;
}
$scope.loadData = function () {
console.log("开始执行任务...");
$q.all([asyncTask1(), asyncTask2(), asyncTask3()])
.then(function (results) {
console.log("所有任务完成,结果:", results);
$scope.data = results;
})
.catch(function (error) {
console.error("任务失败:", error);
});
};
});
运行逻辑
- 点击
loadData()
按钮,开始执行 3 个异步任务:asyncTask1()
需要 2 秒 完成asyncTask2()
需要 1 秒 完成asyncTask3()
需要 1.5 秒 完成
- 所有任务并行执行,不会互相等待。
- 当所有任务都完成后,返回一个 结果数组
["数据 1", "数据 2", "数据 3"]
。 - 如果有任何任务失败,会进入
catch()
,返回错误信息。
控制台输出
开始执行任务...
任务 2 完成 // 1 秒后完成
任务 3 完成 // 1.5 秒后完成
任务 1 完成 // 2 秒后完成
所有任务完成,结果: ["数据 1", "数据 2", "数据 3"]
示例 2:$http
结合 $q.all([...])
angular.module("myApp", [])
.controller("MainController", function ($scope, $http, $q) {
function getUserData() {
return $http.get("https://jsonplaceholder.typicode.com/users/1");
}
function getPosts() {
return $http.get("https://jsonplaceholder.typicode.com/posts?userId=1");
}
function getComments() {
return $http.get("https://jsonplaceholder.typicode.com/comments?postId=1");
}
$scope.loadAllData = function () {
console.log("开始获取数据...");
$q.all([getUserData(), getPosts(), getComments()])
.then(function (responses) {
$scope.user = responses[0].data; // 用户信息
$scope.posts = responses[1].data; // 文章列表
$scope.comments = responses[2].data; // 评论列表
console.log("所有数据加载完毕", $scope);
})
.catch(function (error) {
console.error("数据获取失败", error);
});
};
});
运行逻辑
- 同时请求 用户信息、用户文章、文章评论。
- 所有请求并行执行,不会阻塞。
- 所有请求完成后,数据赋值给
$scope
,然后在 HTML 上渲染出来。 - 如果任何一个请求失败,会进入
catch()
,避免不完整数据加载。
示例 3:某个任务失败时
function asyncTask1() {
var deferred = $q.defer();
setTimeout(() => {
console.log("任务 1 完成");
deferred.resolve("任务 1 数据");
}, 1000);
return deferred.promise;
}
function asyncTask2() {
var deferred = $q.defer();
setTimeout(() => {
console.log("任务 2 失败!");
deferred.reject("任务 2 出错");
}, 1500);
return deferred.promise;
}
$q.all([asyncTask1(), asyncTask2()])
.then(function (results) {
console.log("所有任务完成", results);
})
.catch(function (error) {
console.error("任务失败:", error);
});
运行逻辑
任务 1
成功 (1 秒后完成)。任务 2
失败 (1.5 秒后完成),导致整个$q.all([...])
进入catch()
。- 控制台输出:
任务 1 完成 任务 2 失败! 任务失败: 任务 2 出错
$q.all([...])
的核心总结
支持多个异步任务同时运行,避免阻塞,提高性能。
所有任务都完成后,一次性返回所有结果。
如果任何一个任务失败,整个 $q.all([...])
进入 catch()
,保证数据完整性。
适用于并行加载多个 API、文件或计算任务,提升用户体验。
如果你在 AngularJS 里 需要等待多个数据源都准备好后再执行操作,$q.all([...])
是最佳选择!🚀🚀🚀
二、defer
defer的字面意思是延迟,$q.defer() 可以创建一个deferred实例(延迟对象实例)。
deferred 实例旨在暴露派生的Promise 实例,以及被用来作为成功完成或未成功完成的信号API,以及当前任务的状态。这听起来好复杂的样子,总结$q, defer, promise三者之间的关系如下所示。
var deferred = $q.defer(); //通过$q服务注册一个延迟对象 deferred
var promise = deferred.promise; //通过deferred延迟对象,可以得到一个承诺promise,而promise会返回当前任务的完成结果
defer的方法:
deferred.resolve(value)
成功解决(resolve)了其派生的promise。参数value将来会被用作promise.then(successCallback(value){...}
,errorCallback(reason){...}
,notifyCallback(notify){...})
中successCallback
函数的参数。deferred.reject(reason)
未成功解决其派生的promise。参数reason被用来说明未成功的原因。此时deferred实例的promise对象将会捕获一个任务未成功执行的错误,promise.catch(errorCallback(reason){...})
。补充一点,promise.catch(errorCallback)
实际上就是promise.then(null, errorCallback)
的简写。notify(value)
更新promise的执行状态(原话是provides updates on the status of the promise's execution)
defer的小例子:
function asyncGreet(name) {
// 通过$q.defer()创建一个deferred延迟对象,在创建一个deferred实例时,也会创建出来一个派生的promise对象,
// 使用deferred.promise就可以检索到派生的promise。
var deferred = $q.defer();
deferred.notify('About to greet ' + name + '.'); //延迟对象的notify方法。
if (okToGreet(name)) {
deferred.resolve('Hello, ' + name + '!'); // 任务被成功执行
} else {
deferred.reject('Greeting ' + name + ' is not allowed.'); // 任务未被成功执行
}
return deferred.promise; // 返回deferred实例的promise对象
}
function okToGreet(name) {
//只是mock数据,实际情况将根据相关业务实现代码
if(name == 'Superman') return true;
else return false;
}
// 获得promise对象
var promise = asyncGreet('Superman');
// promise对象的then函数会获得当前任务也就是当前deferred延迟实例的执行状态。
// 它的三个回调函数分别会在resolve(), reject() 和notify()时被执行
promise.then(function(greeting) {
alert('Success: ' + greeting);
}, function(reason) {
alert('Failed: ' + reason);
}, function(update) {
alert('Got notification: ' + update);
});
三、promise
当创建一个deferred实例时,promise实例也会被创建。通过deferred.promise
就可以检索到deferred派生的promise。
promise的目的是允许 interested parties 访问deferred任务完成的结果。
按照CommonJS的约定,promise是一个与对象交互的接口,表示一个动作(action)的结果是异步的,而且在任何给定的时间点上可能或不可能完成。(我理解是promise相当于一个承诺,承诺你这个任务在给定的时间点上可能会完成,也可能完成不了。如果完成了那就相当于resolve, 如果未完成就相当于reject。)
promise 的方法:
then(successCallback, errorCallback, nitifyCallback)
根据promise被resolve/reject,或将要被resolve/reject,调用successCallback/errorCallback。catch(errorCallback) then(null, errorCallback)
的缩写。finally(callback, notifyCallback)
补充说明:
promise.then()
会返回一个新的衍生promise,形成promise链。例如:
promiseB = promiseA.then(function(result) {
return result + 1;
});
// promiseB will be resolved immediately after promiseA is resolved and its value
// will be the result of promiseA incremented by 1