我有一个 promise 对象的 JavaScript 方法。该对象应该第一次被获取,但此后,应该返回一个缓存的实例。为了模拟检索,我们将在此处延迟。
var Promise = require('bluebird');
var retrievedObject = null;
var retrievalCount = 0;
var retrieveObject = Promise.coroutine(
function *retrieveObject(){
if (retrievedObject) {
return retrievedObject;
}
yield Promise.delay(1000); // wait one second
retrievedObject = ++retrievalCount;
return retrievedObject;
});
在这种情况下,为了简单起见,我们只等待一秒钟,然后返回一个递增的计数。如果缓存工作正常,计数应该只递增一次,而之后的所有时间,它应该保持相同的值,即 1。这应该满足我们的需求,作为递增代码是否已被递增的测试机制是否达到。
现在,我还有一个应该测试对象检索的函数,定义如下:
var testRetrievals = Promise.coroutine(
function *testRetrievals() {
var count = yield retrieveObject();
console.log(count);
});
多次调用该函数时,(像这样):
testRetrievals();
testRetrievals();
testRetrievals();
testRetrievals();
testRetrievals();
控制台日志应该是这样的:
1 1 1 1 1
然而,它真正读取的是
1 2 3 4 5
原因很明显。 retrieveObject
方法在第一次调用的 promise 产生之前被调用,因为所有调用启动所需的时间不到一秒钟。但是,我正在寻找一种以某种方式同步 retrieveObject
方法的方法,这样在第一次调用之后,它就不会进入检索过程(在我们的例子中是延迟),而是等待先调用完成。最符合 JavaScript/Node 惯用的方式是什么?我正在考虑引入类似信号量的结构,但我担心这会破坏 Node 的非等待行为。
最佳答案
I'm thinking of introducing semaphore-like structures
是的,你可以做到。我们需要做的主要事情是在调用方法时立即在我们的缓存中创建插槽,而不是在等待检索之后。
因此,在要存储的结果值旁边,我们需要一个pending flag来跟踪我们是否已经开始检索,以及一种通知等待更改的监听器的方法还没有解决的旗帜……
听起来很熟悉?是的,这是一个 promise ,它已经内置了这些信号量。
您只需将 promise 本身存储在缓存中:
var Promise = require('bluebird');
var retrievedPromise = null;
var retrievalCount = 0;
var retrieveObject = Promise.coroutine(function* retrieveObject() {
if (retrievedPromise) {
return yield retrievedPromise;
}
retrievedPromise = Promise.delay(++retrievalCount, 1000); // wait one second
var retrievedObject = yield retrievedPromise;
return retrievedObject;
});
事实上,您甚至不需要带有 yield
的协程:
…
var retrievedPromise = null;
function retrieveObject() {
if (!retrievedPromise)
retrievedPromise = Promise.delay(++retrievalCount, 1000); // wait one second
return retrievedPromise;
}
关于JavaScript 同步 promise 返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34210101/