[英]How to handle async function in redux-observable?
I am using RxJS and redux-observable. 我正在使用RxJS和redux-observable。
I am trying to read file in epic. 我正在尝试读取史诗级的文件。 In my case, I have to do it in epic, because some other epic trigger this epic multiple "unknown" times by expand
operator. 就我而言,我必须在史诗中执行此操作,因为其他一些史诗会由expand
运算符多次触发该史诗。
But since FileReader is async, the code below does not work. 但是由于FileReader是异步的,因此下面的代码不起作用。
What is the correct way especially RxJS way to handle this? 特别是RxJS处理此问题的正确方法是什么? Thanks 谢谢
export const uploadAttachmentEpic = (action$, store) =>
action$
.ofType(UPLOAD_ATTACHMENT)
.map(action => {
const reader = new FileReader();
reader.onload = () => {
return {
...action,
payload: {
...action.payload,
base64: reader.result
}
}
};
reader.readAsDataURL(action.payload.file);
})
.mergeMap(action =>
ajax
.post( /* use action.payload.base64 */ )
.map(uploadAttachmentSucceed)
.catch(uploadAttachmentFailed)
);
Fan's answer (as of this writing) is good but has some caveats to it that are important: 范的答案(截至撰写本文时)是好的,但有一些重要的警告事项:
It starts reading the file immediately instead of lazily. 它立即开始读取文件,而不是懒惰地读取文件。 So just calling readFile(file)
starts it even before anyone has subscribed. 因此,即使在没有任何人订阅之前,只需调用readFile(file)
启动它。 This is error-prone because it's possible that someone might not synchronously subscribe to it right away and then the reader.onload
will miss it. 这很容易出错,因为有人可能没有立即同步订阅它,然后reader.onload
会错过它。 Observables are ideally made completely lazy and repeatable factories. 理想情况下,可观察对象是完全懒惰且可重复的工厂。
It never calls obs.complete()
on the observer, so it's possible the subscription will be a memory leak because it never ends. 它永远不会在观察者上调用obs.complete()
,因此预订可能会因为obs.complete()
而导致内存泄漏。
The methods on the observer are not bound, so reader.onerror = obs.error
won't actually work. 观察者上的方法reader.onerror = obs.error
,因此reader.onerror = obs.error
实际上不会起作用。 Instead you need to either e => obs.error(e)
or obs.error.bind(obs)
See here for reference on why 相反,您需要e => obs.error(e)
或obs.error.bind(obs)
请参阅此处以获取有关为什么的参考
It doesn't abort the reading on unsubscribe. 它不会终止退订时的读取。
Here's how I would do it: 这是我的处理方式:
function readFile(file){
// Could use Observable.create (same thing) but I
// prefer this one because Observable.create is
// not part of the TC39 proposal
return new Observable(observer => {
const reader = new FileReader();
reader.onload = (e) => {
observer.next(reader.result);
// It's important to complete() otherwise this
// subscription might get leaked because it
// "never ends"
observer.complete();
};
reader.onerror = e => observer.error(e);
reader.readAsDataURL(file);
// unsubscribe handler aka cleanup
return () => {
// LOADING state.
// Calling abort() any other time
// will throw an exception.
if (reader.readyState === 1) {
reader.abort();
}
};
});
}
This pattern can be applied to nearly any API, so it's pretty handy to understand exactly how it works. 这种模式几乎可以应用于任何API,因此很容易了解确切的工作方式。
I hope Fan doesn't mind the critique! 我希望范不介意批评! I don't mean to offend, just want to share knowledge. 我并不是要冒犯,只是想分享知识。
Your file reading process does return an Observable. 您的文件读取过程确实返回了一个Observable。 The async process is not handeled properly. 异步过程未正确处理。 I suggest to create an file reading function which return an observable first. 我建议创建一个文件读取函数,该函数首先返回一个可观察的对象。 then attach it to flapMap() 然后将其附加到flipMap()
function readFile(file){
let reader = new FileReader();
return Observable.create(obs => {
reader.onload = function (e) {
obs.next(reader.result);
};
reader.onerror = obs.error;
})
reader.readAsDataURL(file);
}
then in your code you can merge it in like ..flatMap(file=>readFile(file)) 然后可以在您的代码中将其合并为..flatMap(file => readFile(file))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.