我正在使用移动应用,我希望它首先离线。 让我们说我想展示"公司"页面,所以我要求公司数据。
这是我的工作,我在本地存储中寻找公司数据(在我的情况下是indexedDB),同时我为服务器调用相同的数据(以获得潜在的更新)。在每个回调中,我使用获取的数据更新视图。首先从本地数据更新然后从远程数据更新。
为避免竞争条件,我有一个名为" remoteDataAlreadyFetched"的布尔值。我检查我的本地存储回调(如果远程数据在存储响应之前到达)
我的问题是:我该如何处理?我的视图控制器中有2个单独的promise(一个用于本地,一个用于远程)?我应该使用一个可观察的吗?这看起来有点过头了,因为它不会超过2个响应(本地和远程)。我错过了什么吗?
非常感谢你的帮助
编辑:
这就是我用RxJS做的方法。不知道在这种情况下使用Observables是不好的做法......
public static getOfflineFirstObservable(localCall, remoteCall): Observable<any> {
const data$ = new BehaviorSubject(null);
var hasFetchedRemoteData = false;
localCall().then(localDatas => {
if (!hasFetchedRemoteData) data$.next(localDatas);
});
remoteCall().then(remoteDatas => {
data$.next(remoteDatas);
hasFetchedRemoteData = true;
});
return data$.asObservable();
}
答案 0 :(得分:1)
非常有趣的问题,因此您正在尝试确保两个异步操作的顺序。
我实现了一些能够实现这一点的东西,检查下面的代码或运行它们;)
// two promise with no guaranteed order:
var localPromise = new Promise(resolve => setTimeout(resolve, Math.random() * 1000))
var remotePromise = new Promise(resolve => setTimeout(resolve, Math.random() * 1000))
// wrap them
var localFirst = Promise.resolve(localPromise)
// can also add your handler for localdata
.then(() => Promise.resolve(remotePromise))
var remoteFirst = Promise.resolve(remotePromise)
// viola
Promise.race([localFirst, remoteFirst]).then(console.log)
答案 1 :(得分:1)
如果您处于离线状态,那么尝试获取远程数据将导致拒绝承诺(使用promise.race)。如果你要做的就是首先从缓存中获取项目(如果它在那里),如果它没有尝试远程缓存你可以执行以下操作:
const cacheBuilder = promises => fetcher => setFn => getFn => url => {
//using url as key but can be url+JSON.stringify(parameters)+method(GET/POST)
const key = url;
//get from cache first if exist
return getFn(key).catch(()=>{
if(promises[key]){
return promises[key];//return active promise
}
promises[key]=fetcher(url);
return promises[key];
})
.then(
result=>{
if(!promises[key]){//update cache, this will cause requests to server
fetcher(url).then(result=>setFn(key,result)).catch(ignore=>ignore);
}
promises[key]=undefined;
setFn(key,result);
return result;
}
);
}
const cacheFirst = cacheBuilder(
{}//store active promises here
)(
//fetch function (can be $.get or something else)
// I am only using url here but you could use (url,params,method,headers) as well
url=>
//remove "console.log ||" it's to show that multiple active fetches share promises
// asking for fetch("/") multiple times while first time is not resolved
// will not cause multiple requests
console.log("fetching:",url) ||
fetch(url)
.then(response=>response.text())
.then(result=>result.substr(0,10))
)(
//how to set an item in local storage
(key,value)=>{
newStorage = JSON.parse(localStorage.getItem("netCache")||"{}");
newStorage[key]=value;
localStorage.setItem("netCache",JSON.stringify(newStorage));
}
)(
//how to get an item based on key (can be url or url + JSON.stringify(parameters) or url+params+method...)
key=>
Promise.resolve(
JSON.parse(localStorage.getItem("netCache")||"{}")[key] ||
Promise.reject("Not in cache")
)
);
Promise.all([//should not cause multiple requests, should have only one request made
cacheFirst("/"),
cacheFirst("/"),
cacheFirst("/"),
cacheFirst("/")
]).then(
()=>cacheFirst("/")//should come from cache as well, no request made
)
这是一个示例,其中所有实现都在一个函数中,而不传递fetch,getter和setter:
const cacheFirst = (promises => url => {
//using url as key but can be url+JSON.stringify(parameters)+method(GET/POST)
const key = url;
const fetcher = url=>
fetch(url)
.then(response=>response.text())
.then(result=>result.substr(0,10));
const setFn = (key,value)=>{
newStorage = JSON.parse(localStorage.getItem("netCache")||"{}");
newStorage[key]=value;
localStorage.setItem("netCache",JSON.stringify(newStorage));
}
const getFn = key=>
Promise.resolve(
JSON.parse(localStorage.getItem("netCache")||"{}")[key] ||
Promise.reject("Not in cache")
);
//get from cache first if exist
return getFn(key).catch(()=>{
if(promises[key]){
return promises[key];//return active promise
}
promises[key]=fetcher(url);
return promises[key];
})
.then(
result=>{
//update cache if result didn't came from request, this will cause requests to server
if(!promises[key]){
fetcher(url)
.then(result=>setFn(key,result))
.catch(ignore=>ignore);
}
promises[key]=undefined;
setFn(key,result);
return result;
}
);
})({})//IIFE passing in the promises object to store active promises
答案 2 :(得分:0)
我终于决定制定两份单独的承诺。我在页面加载时获取本地数据(如果存在)并立即尝试获取远程数据。因此,用户可以立即看到内容,但仍然看到右上角的微调器指示已经请求了远程数据。当请求返回时,视图会更新。
我已经决定这样做,因为该页面还有一个刷新功能,因此将2个调用(本地和远程)分开以便能够根据需要多次调用服务器似乎更聪明。 / p>
感谢您的回答。 @HMR您的解决方案非常有趣,但并不能满足我的需求。非常感谢