我正在开发一个移动应用程序,但我希望它先离线。
假设我要显示“公司”页面,所以我要求公司数据。
这是我的工作,我在本地存储中查找公司数据(在本例中为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();
}
最佳答案
如果您处于离线状态,则尝试获取远程数据将导致被拒绝的承诺(带有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