The question applies to promises in general and isn't specific to Angular, but the example makes use of Angular $q and service singletons.
Here is 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');
});
});
and the document is
<body ng-app="app">
<div ng-controller="AController"></div>
<div ng-controller="BController"></div>
</body>
It will naturally output
A resolved
B resolved
What would be a good pattern to make the singleton promise resolve only the first time, i.e.
A resolved
and not the subsequent times?
Something like onetimeResolvingService.$$state.status = 2
could possibly can do the trick, but it looks like $q hack and smells bad.
The question applies to promises in general and isn't specific to Angular, but the example makes use of Angular $q and service singletons.
Here is 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');
});
});
and the document is
<body ng-app="app">
<div ng-controller="AController"></div>
<div ng-controller="BController"></div>
</body>
It will naturally output
A resolved
B resolved
What would be a good pattern to make the singleton promise resolve only the first time, i.e.
A resolved
and not the subsequent times?
Something like onetimeResolvingService.$$state.status = 2
could possibly can do the trick, but it looks like $q hack and smells bad.
- From what I read, it doesn't work the way you think. The singleton promise resolves only once. However, two independent .thens, one in each controller, are chained to the promise. The two .thens (more accurately their callbacks) are guaranteed always to respond to the same settlement state - resolved or rejected. Of course, the two controllers can be pletely different from each other in the way they respond to settlement, but that is just stating the obvious. – Roamer-1888 Commented Aug 16, 2015 at 18:55
- This can be done now with Promise.resolve() (see below). – Freewalker Commented May 18, 2017 at 20:02
4 Answers
Reset to default 4What would be a good pattern to make the singleton promise resolve only the first time
To not to. One of the key facets of a promise is that once it's settled, it's settled, and both the settled state (resolved or rejected) and the value are at that point unchanging. See §2.1.2 and §2.1.3 of the A+ promises spec:
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.
If the callbacks added via then
are not satisfied at some stage (e.g., your second hookup), it's not a promise. It's something...else.
T.J. Crowder is correct in that the functionality you're looking for in a promise does not exist. The question of how to achieve what you're looking for however can be found in a structure like below:
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;
};
}
Assuming you new
this once (as angular services do), GetValue() will return the promisified string upon the first call. Subsequent calls return null.
This plunker shows the above in action
Edit: Whoops, misread the question.
There is now a way to do this with EcmaScript promises. The static Promise.resolve() method takes a promise and waits on its value; if it has already been resolved, it simply returns the value.
For example, here's how we're using this method to make multiple calls to fetchQuery rely on a single async authentication call:
fetchQuery gets an auth token (JWT) with:
const authToken = await AuthToken.get();
And AuthToken looks like (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);
}
}
Or simply call then() twice on the same promise object per the OP's question to have two calls wait on the same async operation.
If you're using lodash
you can simply use memoize
like this:
const returnFirstRunResultAlways = _.memoize(async function(params) { ... })
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745115440a4612100.html
评论列表(0条)