Angular的$q, defer, promise

半兽人 发表于: 2025-02-11   最后更新时间: 2025-02-11 15:57:55  
{{totalSubscript}} 订阅, 58 游览

一、$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);
                });
        };
    });

运行逻辑

  1. 点击 loadData() 按钮,开始执行 3 个异步任务:
    • asyncTask1() 需要 2 秒 完成
    • asyncTask2() 需要 1 秒 完成
    • asyncTask3() 需要 1.5 秒 完成
  2. 所有任务并行执行,不会互相等待
  3. 当所有任务都完成后,返回一个 结果数组 ["数据 1", "数据 2", "数据 3"]
  4. 如果有任何任务失败,会进入 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 成功 (1 秒后完成)
  2. 任务 2 失败 (1.5 秒后完成),导致整个 $q.all([...]) 进入 catch()
  3. 控制台输出:
    任务 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 的方法:

  1. then(successCallback, errorCallback, nitifyCallback) 根据promise被resolve/reject,或将要被resolve/reject,调用successCallback/errorCallback。

  2. catch(errorCallback) then(null, errorCallback)的缩写。

  3. 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
更新于 2025-02-11
在线,8小时前登录

查看AngularJS更多相关的文章或提一个关于AngularJS的问题,也可以与我们一起分享文章