Defining Asynchronous Processes Using Flow

Share this video with your friends

Send Tweet
Published 4 years ago
Updated 3 years ago

In real life scenarios, many operations on our data are asynchronous. For example, because additional recourses need to get fetched. MST has first class support for asynchronous actions. We will start with a naively implemented async process, and work through async / await towards MST flows and generators.

In this lesson, you will learn

  • That async process is painful if they need to respect the 'only actions can modify' semantics of MST
  • Flows are the idiomatic way to describe async processes in MST
  • Flows are robust; they make it possible to get full control over the lifecycle of a process

Instructor: [00:00] Now our application has multiple users, but all the wish lists are empty, which is a bit sad. Let's create a small service that can do us some suggestions for our wish list so that we don't have to type them all during this tutorial.

[00:15] To create a service, I'm going to using a small utility, and it's called JSON-server. JSON-server basically takes a small database, a JSON file, and it generates rest service around that. I'm going to fill this database with some data.

[00:33] I have suggestions for women. I know my wife likes the notebooks, so that's probably a good suggestion. Everybody loves Mindstorms. There's also a list of suggestions for males.

[00:45] Now I can simply start the JSON-server, based on this file, and I give the different port, because Create React App ORD already uses 3000s. Now I can simply go to these resources, and this gets me my suggestions. Let's go to the user model, and I'll introduce a new action. I call it getSuggestions.

[01:07] We'll implement it bit later, but first we're going to call it from the UI. We go back to our app, and you say, if there's a selected user, then I want a button. When we click the button, we simply call the action we just introduced. Obviously, this doesn't do anything yet, so let's implement it.

[01:26] Here we have our empty action and we need to fetch the data. We can do that by using windows.fetch. For now, it just hard-codes the URL. The suggestions we need depends on the gender of our user, so we insert self.gender over here.

[01:44] It returns as a promise of a response. From this response, we need a JSON, pass the JSON, and then we actually have our suggestions displaying data.

[01:56] Let's try. If you press the button, we get some raw data back, so that seems right.

[02:03] Let's add these suggestions to the wish list. We can simple [inaudible] in the suggestions, and it will take the suggestions and turn them into wish list entries based on the model definitions of wishlist.items. However, if we try this, you'll see we can get an exception. An exception is that we cannot modify without using an action.

[02:24] The problem is that, because this whole process is asynchronous, this block of cards is no longer executed in the context of an action. By default, mobx-state-tree forbids that, so we should have a separate action to add the item. Let's rewrite this to self.addsuggestions and pass in the suggestions.

[02:44] Now we have an explicit action. This works. We've now got the suggestions added.

[02:53] Of course, we could also rewrite this using async/await. We could write async suggestions, and then the responses awaits the fetch, and suggestions awaits response of JSON. Now we can call self.addsuggestions. We now use async/await and it still works the same.

[03:15] However, the sad thing is that we still cannot inline this over here. The reason is that only the first take of an async process is part of the action. The rest is still asynchronous.

[03:28] However, mobx-state-tree has something more powerful than async/await. It can use generators.

[03:34] Generators are very powerful, because MobX can track a generator and make sure that each tick inside a generator is part of the original action. Let's change this to [inaudible] MobX using generators. To use generators, you need a small utility called flow form [inaudible] state tree. It basically stands for this is an asynchronous process flow.

[03:57] GetSuggestions will be a flow, a generation function. You can recognize generator functions from the start index. The goal is to inline these assignments. In generators, you cannot use await, but you can use yield. Semantically, they do the same.

[04:15] Where we first had async/await, we now have, instead of async, flow generator function. Instead of await, we have yield. Now our new action looks like this.

[04:27] It's still an asynchronous action, but every part of it is executed within the context of the current instance. We can now add suggestions.

[04:37] This works as expected, without exception, and we can safely make those assignments, even though it happens in the far future. Yield, in combination with the generator, makes sure that we re-enter this action and continue executing code in the context of the action we defined.

[04:56] This is how you write asynchronous processes in mobx-state-tree. It works semantically the same as async/wait, so you can wait promises and do all the things you typically do. It's just syntactically a little bit different, but it has lots of potential power.

[05:13] Just to give you a very small sneak preview of that, we're going to log what the flow does at run time. We add quickly a very simple middleware as sneak preview, and we log some basic information of this invocation. Let's see what happens.

[05:33] The getSuggestions action starts. Later on, it resumes after the first yield, it resumes after the second yield, and then it returns.

[05:42] Then the process is finished. We have a very fine-grain control over what happens in our asynchronous processes.