【问题标题】:How to create a initialization function with promises?如何使用承诺创建初始化函数?
【发布时间】:2017-03-22 00:43:10
【问题描述】:

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

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

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

【问题讨论】:

  • 这个函数是一个promise”你的意思是“这个函数返回一个promise”?

标签: javascript promise q


【解决方案1】:
// 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);
        });
    });
}

【讨论】:

  • 我们在哪里使用isInitialisedisInitialising在逻辑中?
  • @Ultrablendz 我们不是,但似乎 OP 对它们有一些用处。如果您不需要状态,可以放心地忽略它们。
【解决方案2】:

已解决/已拒绝的 Promise 将保持其状态(已解决或已拒绝状态),因此您只能使用它来运行初始化代码一次。为此,init() 函数应该返回始终相同的承诺,而不是每次都创建它。

为此,我们在init() 方法之外创建一个延迟对象(initializationDeferred),并在每次调用init() 方法时使用initializationDeferred 返回相同的promise。我们还需要检查 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>

【讨论】:

  • 不,这将运行两次初始化
  • 我已经更新了代码。检查控制台消息,你会看到初始化代码只执行了一次。
  • 是的,initializationStarted 丢失了,感谢您的编辑。
猜你喜欢
  • 1970-01-01
  • 2023-02-26
  • 2020-07-08
  • 1970-01-01
  • 1970-01-01
  • 2018-03-26
  • 1970-01-01
  • 1970-01-01
  • 2016-07-22
相关资源
最近更新 更多