我有一个封装与服务器通信的类 它有一个getOrCreate方法在服务器中创建一个ShoppingCart资源(我使用的是firebase,但我的问题是所有类型的服务器实现)
一旦应用程序加载到客户端(我正在使用角度,但这对于这个问题无关紧要),两个不同的组件(屏幕区域)获取注入它们的服务类(它是单例)和他们调用方法getOrCreateCart() 如果存在cartId,则此方法应检查localStorage,如果存在,则返回它。否则,它应该通过对服务器的异步调用来创建它。 问题是,如果两个组件同时调用此方法,缺少锁定机制,我不能阻止第二个操作,直到第一个操作完成,第二个时间从localStrogate返回cartId而不是创建另一个数据库中的资源
检查数据库中是否已存在cartId不是一个选项,因为cartId是在请求服务器时生成的。
一些代码可以使事情变得更加清晰:
private async getOrCreateCartId() {
let cartId = localStorage.getItem('cartId');
if (cartId) return cartId;
let newCartId = await this.create();
localStorage.setItem('cartId', newCartId);
return newCartId;
}
// Returns a promise with the new created id
private create() {
return this.db.list('/shopping-carts/').push({
dateCreated: new Date().getTime()
});
}
答案 0 :(得分:1)
感谢Roamer-1888,我已经明白了
这是我的解决方案:
我最终得到了这段代码:
private getOrCreateCartId() {
let cartId = localStorage.getItem('cartId');
if (cartId) return Promise.resolve(cartId);
if (this.createPromise) return this.createPromise.then(result => result.key);
this.createPromise = this.create();
this.createPromise.then(result => {
localStorage.setItem('cartId', result.key);
})
return this.createPromise.then(result => result.key);
}
答案 1 :(得分:0)
恭喜你搞清楚了。
这是我最终的结果,首先是模拟你的单身人士:
// singleton FOO
const FOO = (function() {
var cartId = localStorage.getItem('cartId'); // cache, initialised from localStorage
async function getOrCreateCartId() {
if (!cartId) {
cartId = create(); // cache promise
localStorage.setItem('cartId', await cartId);
}
return cartId;
}
// Returns a promise with the new created id
function create() {
return new Promise(function(resolve, reject) {
setTimeout(() => {
resolve(Date.now());
}, 2000);
});
}
return {
'getOrCreateCartId': getOrCreateCartId
};
}());
乍一看,你会认为getOrCreateCartId()
是同步的,但这是async / await的影响,它允许异步代码的结构几乎与同步代码相同。
以下是对FOO.getOrCreateCartId()
// call from component 1
FOO.getOrCreateCartId().then(function(cartId) {
console.log(cartId);
});
// call from component 2
FOO.getOrCreateCartId().then(function(cartId) {
console.log(cartId);
});
即使来自组件1的呼叫需要呼叫create()
,控制台也会记录两次相同的cartId。
DEMO:我添加了.clear()
方法,因此您可以随心所欲地使用它并自行调试它可以正常工作。