Javascript 使用FetchJsonP/Redux/React处理API调用时出错

Javascript 使用FetchJsonP/Redux/React处理API调用时出错,javascript,redux,redux-observable,Javascript,Redux,Redux Observable,嘿,我正试图找出一种方法来处理redux epic中的错误和api调用,我在以下文档中查看了: 我没有任何错误,但什么也没发生代码似乎在循环 /** * Request the JSON guide to the API * and then dispatch requestGuideFulfilled & requestGameTask actions * * @param action$ */ export function requestGuide(action$) {

嘿,我正试图找出一种方法来处理redux epic中的错误和api调用,我在以下文档中查看了: 我没有任何错误,但什么也没发生代码似乎在循环

/**
 * Request the JSON guide to the API
 * and then dispatch requestGuideFulfilled & requestGameTask actions
 *
 * @param action$
 */
export function requestGuide(action$) {
  return action$.ofType(REQUEST_GUIDE)
    .mergeMap(({id}) => fetchJsonp(`${API_URL}/guide/${id}/jsonp`)
      .catch(error => requestGuideFailed(error))
    )
    .mergeMap(response => response.json())
    .mergeMap(json => requestGuideFulfilled(json))
    .map(json => requestGameTask(json))
}


export function manageRequestGuideError(action$) {
  return action$.ofType(REQUEST_GUIDE_FAILED)
    .subscribe(({error}) => {
      console.log('Error',error)
    })
}
有什么想法吗?谢谢大家!

[更新]:即使在抓取时,我也有一个错误:

您提供了一个无效的对象,其中需要流。你可以 提供一个可观察的、承诺的、阵列的或可观察的


有很多问题,所以我会尽我所能详细阐述。坦率地说,RxJS并不容易。我鼓励你在使用redux observable之前花一些时间学习基础知识,当然,除非你只是为了好玩而在空闲时间做实验,而且你喜欢痛苦lol

除非您真的需要复杂的副作用管理,否则不要引入redux observable之类的东西也很重要。有点遗憾的是,文档目前只有简单的示例,但redux observable的真正目的是让复杂的东西,如多路复用WebSocket、复杂的基于时间的排序等变得更容易,而代价是需要非常好地了解RxJS。所以我想我的意思是,如果你真的需要redux,确保你也需要redux是可观察的,或者你可以通过redux thunk逃脱。redux observable sorta的一个制造商劝说人们不要使用它,这可能看起来很有趣,但我只是看到一些疯狂的人使用redux observable/redux saga之类的东西来做一些根本无法证明其复杂性的事情。不过,您最了解自己的需求,所以不要将此视为原则,也不要无理地泄气。看起来RequestGuideCompleted是一个动作创建者,但是这里没有包含源代码,所以我不能确定。mergeMap又名flatMap,它希望您返回另一个流,通常是一个可观察的流,因此操作POJO需要包装在一个可观察的流中,就像Observable.ofrequestGuideFulfilledjson一样,但在这种情况下,使用mergeMap是不必要的。它可能只是一张普通的地图

在redux observable中,所有Epics都必须返回一个observable。此Epic将返回一个订阅,该订阅可观察到订阅的返回值。但是,这实际上会产生一个错误,原因是错误

相反,您可以使用do和ignoreElements来创建一个可观察对象,该对象侦听该操作,记录该属性,然后忽略它,而从不发出任何内容。所以它基本上是只读的

export function manageRequestGuideError(action$) {
  return action$.ofType(REQUEST_GUIDE_FAILED)
    .do(({error}) => {
      console.log('Error',error)
    })
    .ignoreElements();
}
下一个也是最大的问题是你把捕获物放在了哪里。了解使用RxJS意味着我们将观察值链接在一起,这一点很重要。操作符基本上获取一个源并返回另一个观察值,这将在通过它们传输的数据上惰性地应用一些操作。这与使用数组(如arr.map.filter)的函数式编程非常相似,但有两个主要区别:可观察对象是惰性的,它们有时间维度

操作员如何工作 P> >考虑这个可观察链:

Observable.of(1, 2, 3)
  .map(num => num.toString())
  .filter(str => str !== '2');
  .subscribe(value => console.log(value));
我们创建了一个可观测的,当订阅时,将同步发射1,然后发射2,然后发射3。 我们将map操作符应用于该源,该源创建了一个新的可观测值,当订阅时,它本身将订阅我们应用它的源:我们的可观测值1,2,3。 然后,我们将过滤器运算符应用于map返回的可观察对象。正如您可能已经猜到的,filterreturns返回一个新的可观察对象,当订阅时,它本身将订阅我们应用它的源:我们映射的字符串的可观察对象。由于该映射“Observable”本身被应用于一个源,因此它也将订阅其源,输入第一个数字并启动map->filter操作。 将这些中间观测值存储为变量可能会有所帮助,从而稍微揭开一些神秘的面纱

const source1: Observable<number> = Observable.of(1, 2, 3);
const source2: Observable<string> = source1.map(num => num.toString());
const result: Observable<string> = source2.filter(str => str !== '2');
这就产生了一个数字数组数组 我们将每个数字映射到它们自己的两个可观测数字中。所以一个高阶的可观测的,在大多数情况下可能不是我们想要的

Observable.of(1, 3, 5).flatMap(value =>  {
  return Observable.of(value, value + 1);
});
// 1..2..3..4..5..6                                               | Observable<number>
现在让我们把它分解一下

承诺与可观察 您将看到的第一件事是,我将fetchJsonp抽象为getGuide函数。您也可以将此代码放在epic中,但是如果您决定进行测试,将其分离将使您更容易对其进行模拟

我尽可能快地把这一承诺包装在一个可观察的文件中。主要是因为如果我们选择使用RxJS,我们应该全力以赴,尤其是为了防止以后出现混淆。e、 g.Promise和Observable实例都有catch方法,因此如果您开始混合使用这两种方法,很容易导致错误

理想情况下,我们应该使用可观察的,而不是完全的承诺,因为承诺不能被取消,所以您不能取消实际的AJAX请求+JSON解析本身,尽管如果在承诺解决之前将其包装在可观察中并取消订阅,可观察的将正确地忽略承诺稍后解决或拒绝的内容

发出多个动作? 虽然还不是100%清楚,但似乎您打算发出两个动作来响应成功 我正在全力以赴地取回JSON。您前面的代码实际上将JSON映射到RequestGuideCompleted操作,但是下一个操作符将该操作映射到requestGameTask,后者不接收JSON,而是接收RequestGuideCompleted操作。记住上面,关于操作符是如何由可观察的对象链组成的,值是如何通过它们流动的

要解决这个问题,我们需要三思而后行。我们的getGuide Observable将发出一个值,即JSON。给定单个1值,我们希望将其映射到多个其他值,在本例中为两个操作。所以我们想把一个转化为多个。我们需要使用mergeMap、switchMap或concatMap中的一种。在这种情况下,由于我们的getGuide永远不会发出超过一个项目,所有这三个操作符都会有相同的结果,但了解它们是至关重要的,因为这通常很重要,所以请记住!在这种情况下,让我们使用mergeMap

.mergeMap(json => Observable.of(
  requestGuideFulfilled(json),
  requestGameTask(json)
))
Observable.of支持任意数量的参数,并将按顺序发出每个参数

.catch(error => Observable.of(
  requestGuideFailed(error)
))
捕捉错误 因为我们可以观察到的链条是……嗯……链条,呵呵,价值观是在它们之间传递的。正如我们在上面了解到的,因此,在这些链中放置错误处理的位置非常重要。这实际上与传统异常的错误处理,甚至承诺之间没有太大区别,但承诺没有运算符,所以人们通常不会遇到这种混乱

catch操作符是最常见的,它与catch承诺非常相似,只是您必须返回所需值的可观察值,而不是值本身。OfOfOf在这里很常见,因为我们通常只想按顺序发出一个或多个项目

.catch(error => Observable.of(
  requestGuideFailed(error)
))
每当我们将此运算符应用到的源发出错误时,它将捕获该错误,而不是发出requestGuideFailederror,然后完成

因为它会对错误发出操作,所以我们应用于此.catch**结果的任何运算符也可能会对catch发出的值进行操作

虽然redux observable不是唯一的,因为redux observable只是一个很小的库和一个约定,但使用RxJS,您经常会看到Epics遵循类似的模式

侦听特定的操作 然后,将该动作合并或切换到执行副作用的内部可观察对象 当副作用成功时,我们将其映射为成功的行动 如果出现错误,我们会在mergeMap/switchMap中放置一个catch,但通常位于内链的末端,这样我们发出的任何动作都不会在发生意外时转换。 您将有望从redux可观察文档中识别出这种一般模式:

function exampleEpic(action$) {
  return action$.ofType(EXAMPLE)
    .mergeMap(action =>
      getExample(action.id)
        .map(resp => exampleSuccess(resp))
        .catch(resp => Observable.of(
          exampleFailure(resp)
        ))
    );
}
将这些知识应用到我们以前的工作中:

getGuide(id)
  .mergeMap(json => Observable.of(
    requestGuideFulfilled(json),
    requestGameTask(json)
  ))
  .catch(error => Observable.of(
    requestGuideFailed(error)
  ))
我想就是这样

呸!对不起,那话太啰嗦了。这完全有可能是你知道的,所以如果我在唱诗班布道,请原谅我!我开始写一些简短的东西,但在澄清之后不断添加澄清。哈哈

如果你在苦苦挣扎,一定要确保使用RxJS和redux observable或任何复杂的中间件对你的应用来说都是必要的复杂性。

非常感谢你给出了这个非常清晰的答案!!!!!那工作做得很好!你一定花了很多时间,但我非常感激!
.mergeMap(json => Observable.of(
  requestGuideFulfilled(json),
  requestGameTask(json)
))
.catch(error => Observable.of(
  requestGuideFailed(error)
))
getJsonSomehow()
  .catch(error => Observable.of(
    someErrorAction(error)
  ))
  .map(json => {
    // might be the JSON, but also might be the
    // someErrorAction() action!
    return someSuccessAction();
  })
function exampleEpic(action$) {
  return action$.ofType(EXAMPLE)
    .mergeMap(action =>
      getExample(action.id)
        .map(resp => exampleSuccess(resp))
        .catch(resp => Observable.of(
          exampleFailure(resp)
        ))
    );
}
getGuide(id)
  .mergeMap(json => Observable.of(
    requestGuideFulfilled(json),
    requestGameTask(json)
  ))
  .catch(error => Observable.of(
    requestGuideFailed(error)
  ))