_Question_
I have read Creating asynchronous actions. The main reason we can't use async/await is that modification operations to state tree in async/await function cann't be detech automatically. so, i think we can write like below to continue use async/await by explicitly wrap operations in a action.I am not sure if there are potential side effects? If it's ok, i hope MST can provider natively the runInAction interface to the model type
const Store = types.model({
githubProjects: types.array(types.frozen),
state: types.enumeration("State", ["pending", "done", "error"])
})
.actions(self => ({
runInAction(action) {
action.call(self)
}
}))
.actions(self => ({
async fetchProjects() {
self.githubProjects = []
self.state = "pending"
try {
const projects = await fetchGithubProjectsSomehow()
self.runInAction(() => {
self.state = "done"
self.githubProjects = projects
})
} catch (error) {
console.error("Failed to fetch projects", error)
self.runInAction(() => {
self.state = "error"
})
}
}
}))
Not sure if I understood right but this runInAction wrapping is already done if you use flow.
Not sure if I understood right but this runInAction wrapping is already done if you use flow.
馃槰...flow receive a generator function instead of an async function as it's parameter.
Yes, but as mentioned in the docs, it's as simple as replacing async function x() {/*...*/} with flow(function* x() {/*...*/}), and await with yield.
The generator version of this action in the docs is shorter and simpler than the one using async+runInAction, _and_ unlike async implementation it actually counts as 1 action (which it should!). This is an important part of why you shouldn't use the proposed async+runInAction pattern:
Imagine you have an onAction handler for your instance. If your code outside calls fetchProjects once, you would expect that onAction gets triggered once, but each runInAction step triggers an additional onAction (see this sandbox) which is not ideal / expected behavior.
This also breaks any notion of using actions as atomic transactions; see this sandbox.
I'm not sure why you would insist on using async/await _inside_ the action... you can still use it as much as you want outside:
// in the model actions:
fetchAThing: flow(function* fetchAThing(thingId) {
const thingFromServer = yield fetchThingFromServer(thingId)
self.thing = thingFromServer
})
// ... somewhere outside the model:
async function fetchThingFromServer(thingId) {
const response = await callTheServer(thingId)
const thing = extractThing(response)
return thing
}
@beaulac Thank you very much for your answers and examples. flow is able to keep the action transactional, which is enough to convince me to use flow instead of runInAction + async.
Also something worth noting is that unlike await, with TypeScript the compiler is unable to infer a type from a yield. It's not a huge issue but does mean it's not as simple as just swapping one for the other.
Great point 鈥撀爄t seems TS is a ways off inferring intermediate yields despite the improvements in 3.6.
As an alternative, it might be worth looking at actionAsync which allows async/await actions (so, with type inference)
for Mobx, though as as mentioned here by the author it would have to be re-implemented for MST.
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs or questions.
Most helpful comment
Yes, but as mentioned in the docs, it's as simple as replacing
async function x() {/*...*/}withflow(function* x() {/*...*/}), andawaitwithyield.The generator version of this action in the docs is shorter and simpler than the one using async+
runInAction, _and_ unlike async implementation it actually counts as 1 action (which it should!). This is an important part of why you shouldn't use the proposed async+runInActionpattern:Imagine you have an
onActionhandler for your instance. If your code outside callsfetchProjectsonce, you would expect thatonActiongets triggered once, but eachrunInActionstep triggers an additionalonAction(see this sandbox) which is not ideal / expected behavior.This also breaks any notion of using actions as atomic transactions; see this sandbox.
I'm not sure why you would insist on using async/await _inside_ the action... you can still use it as much as you want outside: