如何创建带有承诺的初始化函数?

How to create a initialization function with promises?

我需要一个模块只调用一次的初始化函数。此函数是一个承诺,由执行函数调用。如果执行两次,第二次必须等待初始化,然后继续执行。

我写了这段代码,但是第二次调用 execute 总是在等待,而且永远不会 return。我错过了什么?

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;

不正确。它不等待任何事情,它只是从 q.promise 构造函数中退出并且不做任何事情。你似乎也使用了 Promise constructor antipattern.

你应该做的是

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);
        });
    });
}

resolved/rejected 承诺将保持其状态(已解决或拒绝状态),因此您可以将它用于 运行 初始化代码一次。为此,init() 函数应该 return 总是相同的承诺 而不是每次都创建它。

为此,我们在init()方法之外创建一个延迟对象(initializationDeferred),并使用initializationDeferred到return相同的promise 每次调用 init() 方法。我们还需要检查 init() 之前是否已经完成,我们使用共享变量 initializationStarted 跳过 setTimeout 如果已经在之前的调用中完成。

现在,在 execute 中,您可以确定 then()onFulfilled 回调仅在初始化 init() 方法时被调用。

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>