我正在开发一个移动应用程序,但我希望它先离线。
假设我要显示“公司”页面,所以我要求公司数据。

这是我的工作,我在本地存储中查找公司数据(在本例中为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

10-06 15:01