I need a initialization function called only one time for a module. This function is a promise and is called by an execute function. If execute is called twice, the second must wait the initialization then continue the execution.
I wrote this code, but the second call of execute is always waiting and doesn't never return. What have i missed?
var initialized = false;
var initializing = false;
var initializationPromise;
var init = function () {
initializing = true;
return q.promise(function (resolve) {
// simulate initialization
setTimeout(function () {
// initialized
initialized = true;
resolve();
}, 1000);
}).fin(function () {
initializing = false;
});
};
var execute = function () {
return q.promise(function (resolve, reject, notify) {
if (initialized) {
// already initialized
resolve();
} else {
if (!initializing) {
// initializing
initializationPromise = init().then(function () {
// simulate execution
setTimeout(function () {
resolve();
}, 1000);
}, function (reason) {
reject(reason);
});
} else {
// Wait : initializing in progress
return initializationPromise;
}
}
});
};
execute().then(function () {
// This is executed
});
execute().then(function () {
// This is never executed
});
// Wait : initializing in progress return initializationPromise;
is not correct. That doesn't wait for anything, it just drops out of the q.promise
constructor and does not do anything. Also you seem to employ the Promise
constructor antipattern .
What you should do instead is
var initialisationPromise = null;
function isInitialised() {
return initialisationPromise != null && initialisationPromise.isFulfilled();
}
function isInitialising() {
return initialisationPromise != null && initialisationPromise.isPending();
}
function init() {
// init can be called as often as necessary, and returns when it's done
if (initialisationPromise == null) { // do the test here!
// this part runs only once
initialisationPromise = q.promise(function (resolve) {
// simulate initialization
setTimeout(function () {
// initialized
resolve();
}, 1000);
});
}
return initialisationPromise;
}
function execute() {
return init().then(function () {
return q.promise(function(resolve, reject, notify) {
// simulate execution
setTimeout(function () {
resolve();
}, 1000);
});
});
}
A resolved/rejected promise will maintain its state (resolved or rejected state), so you can use it to run the initialization code only once. To do that, the init()
function should return always the same promise and not create it every time.
For this reason, we create a deferred object ( initializationDeferred
) outside the init()
method and use initializationDeferred
to return the same promise every time init()
method is called. We need, also, to check if the init()
has been already done before, we use the shared variable initializationStarted
to skip the setTimeout
if already done in a previous invocation.
Now, inside execute
you can be sure that the onFulfilled callback of then()
is called only when init()
method is initialized.
var initializationDeferred = Q.defer(); // Create here the deferred object so it's common to all init() invocations var initializationStarted = false; var init = function() { if (!initializationStarted) { initializationStarted = true; setTimeout(function() { // initialized console.log('Init timeout fired!'); initializationDeferred.resolve(true); // Resolve the promise associated to the deferred object }, 1000); } return initializationDeferred.promise; // Return the promise associated to the deferred object }; var execute = function() { return init().then(function(initialized) { // Here your module is initialized and you can do whatever you want // The value of "initialized" here is always "true" console.log('Execute: initialized?', initialized); }); }; execute().then(function() { // This is executed console.log('Execute First invocation'); }); execute().then(function() { // This is executed too console.log('Execute Second invocation'); });
<script src="http://cdnjs.cloudflare.com/ajax/libs/q.js/0.9.2/q.js"></script>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.