一次性解析 promise 单例(Angular 服务)

One-time resolving promise singleton (Angular service)

这个问题一般适用于承诺,并不特定于 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>

自然会输出

A resolved

B resolved

什么是使单例承诺仅在第一次解析的好模式,即

A resolved

而不是以后的时间?

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

What would be a good pattern to make the singleton promise resolve only the first time

不要。承诺的一个关键方面是,一旦它被解决,它就被解决了,并且解决状态(解决或拒绝)和价值在那个时候都不会改变。参见 the A+ promises spec:

的 §2.1.2 和 §2.1.3

2.1.2 When fulfilled, a promise:

  2.1.2.1 must not transition to any other state.

  2.1.2.2 must have a value, which must not change.

2.1.3 When rejected, a promise:

  2.1.3.1 must not transition to any other state.

  2.1.3.2 must have a reason, which must not change.

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

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

    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() 将在第一次调用时 return 承诺的字符串。后续调用 return null.

This plunker 显示上述内容

编辑:糟糕,看错问题了。


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

例如,下面是我们如何使用此方法根据单个异步身份验证调用对 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() 两次,让两个调用等待同一个异步操作。

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

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