【问题标题】:One-time resolving promise singleton (Angular service)一次性解决承诺单例(Angular 服务)
【发布时间】:2015-08-16 16:34:42
【问题描述】:

这个问题一般适用于 Promise,并不特定于 Angular,但该示例使用了 Angular $q 和服务单例。

这里是a plunker

var app = angular.module('app', []);

app.factory('onetimeResolvingService', function ($q) {
  var promise = $q(function(resolve, reject) {
    setTimeout(function () {
      resolve();
    }, 500);
  });

  return promise;
});

app.controller('AController', function (onetimeResolvingService, $q) {
  onetimeResolvingService.then(function () {
    console.log('A resolved');
    return $q.reject();
  }).then(function () {
    console.log('A resolved');
  });
});

app.controller('BController', function (onetimeResolvingService, $q) {
  onetimeResolvingService.then(function () {
    console.log('B resolved');
    return $q.reject();
  }).then(function () {
    console.log('B resolved');
  });
});

文件是

  <body ng-app="app">
    <div ng-controller="AController"></div>
    <div ng-controller="BController"></div>
  </body>

自然会输出

已解决

B 已解决

什么是让单例承诺只在第一次解决的好模式,即

已解决

而不是以后的时间?

onetimeResolvingService.$$state.status = 2 之类的东西可能可以解决问题,但它看起来像 $q hack 并且闻起来很糟糕。

【问题讨论】:

  • 根据我的阅读,它不像你想象的那样工作。单例承诺只解决一次。然而,两个独立的 .then,一个在每个控制器中,被链接到 Promise。这两个 .thens(更准确地说是它们的回调)保证始终响应 same 结算状态 - 已解决或已拒绝。当然,这两个控制者对结算的响应方式可能完全不同,但这只是显而易见的。
  • 现在可以使用 Promise.resolve() 来完成(见下文)。

标签: javascript angularjs promise angular-promise


【解决方案1】:

什么是让单例承诺只在第一次解决的好模式

不要。 Promise 的一个关键方面是,一旦它被解决,它就被解决了,并且已解决的状态(已解决或被拒绝)和值在那时都不会改变。请参阅the A+ promises spec 的 §2.1.2 和 §2.1.3:

2.1.2 实现时,一个承诺:

  2.1.2.1 不得转换到任何其他状态。

  2.1.2.2 必须有一个值,该值不得更改。

2.1.3 当被拒绝时,一个承诺:

  2.1.3.1 不得转换到任何其他状态。

  2.1.3.2 必须有原因,不能改变。

如果通过then 添加的回调在某个阶段不满足(例如,您的第二次连接),这不是一个承诺。这是……别的东西。

【讨论】:

    【解决方案2】:

    T.J. Crowder 是正确的,因为您在 Promise 中寻找的功能不存在。然而,如何实现您正在寻找的问题可以在如下结构中找到:

        function OnetimeValue($q) {
            var promisedValue = $q(function(resolve, reject) {
                setTimeout(function () {resolve('The one time value');}, 500);
            });
    
            var valueGot = false;
            this.GetValue = function GetValue() {
                var res;
                if (!valueGot) {
                    res = promisedValue;
                } else {
                    res = $q(function(resolve, reject) {
                        resolve(null);
                    });
                }
                valueGot = true;
    
                return res;
            };
        }
    

    假设你 new 一次(就像 Angular 服务所做的那样),GetValue() 将在第一次调用时返回承诺的字符串。后续调用返回 null。

    This plunker 展示了上述内容

    【讨论】:

    • 我想在我的情况下必须是else { res = $q(function(resolve, reject) { reject(); }); } 才能跳过GetValue().then 中的验证,但这个想法很好,似乎是唯一合理的解决方案,我倾向于类似的解决方案。
    • 只要确保您没有拒绝出于控制流目的的承诺。从语义上讲,reject 表示发生了错误。因此,如果在您的域逻辑中多次访问该值实际上是错误的,那么就去做吧。
    • 与其说是“一次价值”,不如说是“第一次之后的价值将是null”:-)
    • 哈——没有领域知识很难猜出什么是有意义的。
    【解决方案3】:

    编辑:糟糕,误读了问题。


    现在有一种方法可以使用 EcmaScript 承诺来做到这一点。静态 Promise.resolve() 方法接受一个 Promise 并等待它的值;如果它已经被解析,它只是返回值。

    例如,以下是我们如何使用此方法对 fetchQuery 进行多次调用依赖于单个异步身份验证调用:

    fetchQuery 通过以下方式获取身份验证令牌 (JWT):

    const authToken = await AuthToken.get();
    

    而 AuthToken 看起来像(TypeScript):

    class AuthToken {
      private static _accessTokenPromise: Promise<string>;
    
      public static async get() {
        if (!this._accessTokenPromise)
          this._accessTokenPromise = this.AuthFunction(); // AuthFunction returns a promise
    
        return await Promise.resolve(this._accessTokenPromise);
      }
    }
    

    或者根据 OP 的问题在同一个 promise 对象上调用 then() 两次,让两个调用等待同一个异步操作。

    【讨论】:

    • 这是一个很老的问题,但我不确定这不是我的情况。事实上,这是必须避免的行为。在您的示例中,AuthToken.get().then(() =&gt; console.log('foo'); AuthToken.get().then(() =&gt; console.log('bar') 将导致 'foo bar' 输出。虽然预期的行为是'foo'。这就是“一次性解决”所指的内容,这就是 OP 中的示例所说明的内容。
    • @estus 你是对的,仔细阅读,OP 要求与我想要的相反的行为(防止 this() 被调用两次,而不是调用 this() 两次以获得两个放置在等待相同异步操作的代码中)。
    • 这实际上是我的老问题,只是很好奇如何实现这种承诺模式(事实上该模式证明自己不可行)。
    【解决方案4】:

    如果您使用的是lodash,您可以像这样简单地使用memoize

    const returnFirstRunResultAlways = _.memoize(async function(params) { ... })
    

    【讨论】:

    猜你喜欢
    • 2018-08-16
    • 2015-08-12
    • 2015-12-06
    • 2020-01-16
    • 1970-01-01
    • 2018-03-07
    • 2017-03-01
    • 2017-01-20
    • 1970-01-01
    相关资源
    最近更新 更多