简体   繁体   中英

How to create a initialization function with promises?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM