javascript – 如何防止从史诗开始两次相同的调用

调度REMOTE_DATA_STARTED操作时,我的史诗醒来,并使用action.url和action.owner获取数据.

我需要确保我不会发起对同一所有者/网址的两个并发调用.完成对所有者/网址的调用后,可以稍后为同一所有者/网址启动另一个.

取消不是我在这里寻找的,因为我不想取消现有的请求,我想阻止开始新的请求.

我觉得我需要混合使用exhaustMap和groupBy,但我不知道从哪里开始.

这是我的史诗,它拒绝所有并发呼叫,而不是所有者/网址

const myEpic = action$=>
  action$.ofType("REMOTE_DATA_STARTED").exhaustMap(action =>
    fakeAjaxCall().map(() => {
      return { type: "COMPLETED", owner: action.owner, url: action.url };
    })
  );

试一试

我用一个失败的测试用例创建了这个测试项目.你能帮我做这个吗?

https://codesandbox.io/s/l71zq6x8zl

正如您将看到的,test1_exhaustMapByActionType_easy工作正常,它的test2_exhaustMapByActionTypeOwnerAndUrl失败了.

确保展开控制台以查看测试结果.

解决方法:

好的,我们走了:

GroupBy req.owner,将结果展平:

const myEpic = action$=>
  action$
    .ofType("REMOTE_DATA_STARTED")
    .groupBy(req => req.owner)
    .flatMap(ownerGroup => ownerGroup.groupBy(ownerReq => ownerReq.url))
    .flatMap(urlGroup => 
      urlGroup.exhaustMap(action => 
        fakeAjaxCall().map(() => ({ type: "COMPLETED", owner: action.owner, url: action.url }))
      )
    )

不要忘记observe.complete();

const test1_exhaustMapByActionType_easy = () => {
  const action$= new ActionsObservable(
    Observable.create(observer => {
      observer.next({ type: "REMOTE_DATA_STARTED", owner: "ownerX", url: "url1" });
      observer.next({ type: "REMOTE_DATA_STARTED", owner: "ownerX", url: "url1" });
      setTimeout(() => {
        observer.next({ type: "REMOTE_DATA_STARTED", owner: "ownerX", url: "url1" });
        observer.complete();
      }, 30);
    })
  );

  const emittedActions = [];
  const epic$= myEpic(action$);

  epic$.subscribe(action => emittedActions.push(action), null, () => expect("test1_exhaustMapByActionType_easy", 2, emittedActions));
};

同样在这里:

const test2_exhaustMapByActionTypeOwnerAndUrl = () => {
  const action$= new ActionsObservable(
    Observable.create(observer => {
      // owner1 emmits 4 concurrent calls, we expect only two to COMPLETED actions; one per URL:
      observer.next({ type: "REMOTE_DATA_STARTED", owner: "owner1", url: "url1" });
      observer.next({ type: "REMOTE_DATA_STARTED", owner: "owner1", url: "url1" });
      observer.next({ type: "REMOTE_DATA_STARTED", owner: "owner1", url: "url2" });
      observer.next({ type: "REMOTE_DATA_STARTED", owner: "owner1", url: "url2" });

      // owner2 emmits 2 calls at the same time as owner 1. because the two calls
      // from owner2 have the same url, we expecty only one COMPLETED action
      observer.next({ type: "REMOTE_DATA_STARTED", owner: "owner2", url: "url1" });
      observer.next({ type: "REMOTE_DATA_STARTED", owner: "owner2", url: "url1" });

      // Once all of the above calls are completed each owner makes one concurrent call
      // we expect each call to go throught and generate a COMPLETED action
      setTimeout(() => {
        observer.next({ type: "REMOTE_DATA_STARTED", owner: "owner1", url: "url1" });
        observer.next({ type: "REMOTE_DATA_STARTED", owner: "owner2", url: "url1" });
        observer.complete();
      }, 30);
    })
  );

  const emittedActions = [];
  const epic$= myEpic(action$);

  epic$.subscribe(action => emittedActions.push(action), null, () => expect("test2_exhaustMapByActionTypeOwnerAndUrl", 5, emittedActions));
};

Full sample

上一篇:javascript – Subject vs AnonymousSubject


下一篇:javascript – 如何跟踪角度项目中的“EmptyError:无顺序元素”错误