简体   繁体   English

如何在redux-observable中正确使用forkJoin?

[英]How to use forkJoin correctly in redux-observable?

I am using redux-observable. 我正在使用redux-observable。

I am trying to send mail after all attachments be uploaded separately. 所有附件分别上传后,我正在尝试发送邮件。

The process of this send mail API is 此发送邮件API的过程是

  1. Add mail content to the draft 将邮件内容添加到草稿中
  2. Add attachments separately to the draft 将附件分别添加到草稿中
  3. Once all attachments are uploaded, send mail from draft 上传所有附件后,从草稿发送邮件

Right now the code below can upload all attachments separately successfully. 现在,下面的代码可以成功地分别上传所有附件。 However, I don't know how to wait them all done. 但是,我不知道如何等待它们完成。

After learning this doc , I think I need use forkJoin , however I didn't figure out how to use it correctly in this case. 学习forkJoin 此文档后 ,我认为我需要使用forkJoin ,但是在这种情况下我没有弄清楚如何正确使用它。

export const addMailContentSucceedEpic = (action$, store) =>
  action$
    .ofType(ADD_MAIL_CONTENT_SUCCEED)
    .expand(action => {     // expand here helps upload all attachments separately
      const restAttachments = removeFirstAttachment(action.payload.attachments);

      if (isEmpty(restAttachments)) return Observable.empty();

      return Observable.of({
        ...action,
        payload: {
          ...action.payload,
          attachments: restAttachments
        }
      });
    })
    .map(action => addAttachment({ mailId: action.payload.mailId, attachment: first(action.payload.attachments) }));

export const addAttachmentEpic = (action$, store) =>
  action$
    .ofType(ADD_ATTACHMENT)
    .mergeMap(action => getBase64FromFile(action))
    .mergeMap(action =>
      ajax
        .patch(url, { base64: action.payload.base64 })
        .map(() => addAttachmentSucceed({ mailId: action.payload.mailId }))
        .catch(addAttachmentFailed)
    );

export const addAttachmentSucceedEpic = (action$, store) =>
  action$
    .ofType(ADD_ATTACHMENT_SUCCEED)
    // Here is wrong, I need wait all attachments be uploaded
    // I tried to add a global `let = tasks$[];` on the top, but I am not clear where and how I can push each task (add attachment) to it
    // Then add .mergeMap(action => Observable.forkJoin(tasks$)) here, but it probably wrong to be added after .ofType(ADD_ATTACHMENT_SUCCEED)
    // In my mind, it needs to be after something `ADD_ALL_ATTACHMENTS_SUCCEED`
    .map(action => sendMailFromDraft({ mailId: action.payload.mailId }));

UPDATE : 更新

I will try to change to the structure below. 我将尝试更改为以下结构。

Maybe I can pass addAttachments$ as a payload(?) or create it as a global variable. 也许我可以将addAttachments$作为有效负载(?)传递或将其创建为全局变量。 I will give more update later. 稍后我会提供更多更新。

const addAttachments$ = [
  ajax.patch(url, { base64: getBase64(first(action.payload.attachments) })),
  ajax.patch(url, { base64: getBase64(second(action.payload.attachments) })),
  ajax.patch(url, { base64: getBase64(third(action.payload.attachments) })),
  // ...
];

export const addMailContentSucceedEpic = (action$, store) =>
  action$
    .ofType(ADD_MAIL_CONTENT_SUCCEED)
    .mergeMap(action =>
      Observable.forkJoin(addAttachments$)
      .map(() => sendMailFromDraft({ mailId: action.payload.mailId }))
    )
    .catch(/* ... */);

This is my final solution. 这是我的最终解决方案。

Since read file is also async, so there are two forkJoin in this solution. 由于读取文件也是异步的,因此此解决方案中有两个forkJoin

One forkJoin is used to wait all files are read. 一个forkJoin用于等待读取所有文件。 The other forkJoin is used for waiting all attachments are uploaded. 另一个forkJoin用于等待所有附件上载。

For getBase64FromFile$ in the code, please check How to handle async function in redux-observable? 对于代码中的getBase64FromFile$ ,请检查如何在redux-observable中处理异步函数?

export const addMailContentSucceedEpic = (action$, store) =>
  action$
    .ofType(ADD_MAIL_CONTENT_SUCCEED)
    .mergeMap(action => addTasks$(action))
    .mergeMap(action => Observable.forkJoin(action.payload.posts$)
      .map(() => sendMailFromDraft({ mail: action.payload.mail }))
    );


function getPosts(base64Array, action) {
  let posts$ = [];

  for (let i = 0; i < base64Array.length; ++i) {
    posts$ = [...posts$, ajax.patch(url, { base64: base64Array[i] })];
  }

  return {
    ...action,
    payload: {
      ...action.payload,
      posts$
    }
  };
}

function addTasks$(action) {
  let readFiles$ = [];

  for (let i = 0, attachmentIds = Object.keys(action.payload.attachments); i < attachmentIds.length; ++i) {
    const attachmentId = attachmentIds[i];
    const attachment = action.payload.attachments[attachmentId];

    readFiles$ = [...readFiles$, getBase64FromFile$(attachment)];
  }

  return Observable.forkJoin(readFiles$)
    .map(base64Array => getPosts(base64Array, action));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM