如何在 redux-observable 中正确使用 forkJoin?
How to use forkJoin correctly in redux-observable?
我正在使用 redux-observable。
我正在尝试在所有附件分别上传后发送邮件。
本次发送邮件API的过程是
- 将邮件内容添加到草稿
- 在草稿中单独添加附件
- 上传所有附件后,从草稿发送邮件
现在下面的代码可以成功上传所有附件。但是,我不知道如何等待它们全部完成。
学习了this doc后,我想我需要使用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 }));
更新:
我会尝试改成下面的结构
也许我可以将 addAttachments$
作为有效负载(?)传递或将其创建为全局变量。稍后我会提供更多更新。
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(/* ... */);
这是我的最终解决方案。
由于读取文件也是异步的,所以这个解决方案中有两个forkJoin
。
一个forkJoin
用于等待所有文件被读取。另一个forkJoin
用于等待所有附件上传。
代码中的getBase64FromFile$
请勾选
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));
}
我正在使用 redux-observable。
我正在尝试在所有附件分别上传后发送邮件。
本次发送邮件API的过程是
- 将邮件内容添加到草稿
- 在草稿中单独添加附件
- 上传所有附件后,从草稿发送邮件
现在下面的代码可以成功上传所有附件。但是,我不知道如何等待它们全部完成。
学习了this doc后,我想我需要使用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 }));
更新:
我会尝试改成下面的结构
也许我可以将 addAttachments$
作为有效负载(?)传递或将其创建为全局变量。稍后我会提供更多更新。
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(/* ... */);
这是我的最终解决方案。
由于读取文件也是异步的,所以这个解决方案中有两个forkJoin
。
一个forkJoin
用于等待所有文件被读取。另一个forkJoin
用于等待所有附件上传。
代码中的getBase64FromFile$
请勾选
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));
}