Reswift: Concurrency, custom dispatch queue

Created on 7 Jul 2020  路  7Comments  路  Source: ReSwift/ReSwift

Question

I have multiple WarmUp actions dispatched one after another here

The 2nd action is expected to perform based on the state changes by the 1st action, in this case, the 2nd WarmUp action should not go inside this block because the appState.needsWarmup is already updated to false by the 1st action

Everything works fine when I directly use the Store.dispatch, but things work weirdly after I tried to create my custom serial dispatch queue for actions.

Expected - Store.dispatch()

Console logs

Warmup-ing ...
doneWarmup
Done warmup ...
warmup
warmup

Unexpected - Serial Dispatch Queue

Console logs

Warmup-ing ...
warmup
Warmup-ing ...
warmup
Warmup-ing ...
doneWarmup
Done warmup ...
doneWarmup
Done warmup ...
doneWarmup
Done warmup ...

I've put the expectations also here in the code comment
https://github.com/MichaelGuoXY/ReSwiftExample/blob/master/ReSwiftExample/ReSwift/ReSwift.swift#L85-L111

I'm not sure if it's the right way that I tried, please do advice on how to use a custom serial queue for actions & state changes

Sample

https://github.com/MichaelGuoXY/ReSwiftExample

Please download and try, no pod install required

Most helpful comment

The issue you're experiencing is being caused by your use of mainStore.dispatch inside of middleware.
Middleware has its own dispatch function available (as seen in the closure parameters) that you should be using instead, and indeed fixes the problem.

https://github.com/MichaelGuoXY/ReSwiftExample/blob/bdad901e2018ab87f4c6cb4683042e81e6f217f3/ReSwiftExample/ReSwift/ReSwift.swift#L46

Change mainStore.dispatch(AppAction.doneWarmup) to dispatch(AppAction.doneWarmup)

By dispatching again on mainStore you were adding the doneWarmup to the END of your queue, which was already stacked with 3x warmup actions, and because it was processing those first, it was also dispatching 3x doneWarmup.

In fact, you don't have any invalid concurrency issues occurring, which is why the fatal error is not happening. Your code is perfectly safe, just invalid logically for what you want.

using the provided dispatch allows the doneWarmup to happen immediately during the processing of the first warmup, and fixes the bug.

All 7 comments

It's easy to understand with Main thread, the 2nd action will be dispatched after the 1st action being dispatched and states being updated. But how can I achieve the same thing with a custom serial queue?

Why do you need to dispatch three actions in a row instead of just one?

Why do you need to dispatch three actions in a row instead of just one?

There are cases the same action can be dispatched at almost the same time from different places. No need to be like what's in the example, this is just to reproduce the issue more easily.

I'm not sure if I understand the problem, but in the middleware, you're dispatching an action before having finished processing the previous one (you're calling next(action) at the end). You can call next whenever you want, and then dispatch more actions in your middleware, but the order is important. Does that help?

The issue you're experiencing is being caused by your use of mainStore.dispatch inside of middleware.
Middleware has its own dispatch function available (as seen in the closure parameters) that you should be using instead, and indeed fixes the problem.

https://github.com/MichaelGuoXY/ReSwiftExample/blob/bdad901e2018ab87f4c6cb4683042e81e6f217f3/ReSwiftExample/ReSwift/ReSwift.swift#L46

Change mainStore.dispatch(AppAction.doneWarmup) to dispatch(AppAction.doneWarmup)

By dispatching again on mainStore you were adding the doneWarmup to the END of your queue, which was already stacked with 3x warmup actions, and because it was processing those first, it was also dispatching 3x doneWarmup.

In fact, you don't have any invalid concurrency issues occurring, which is why the fatal error is not happening. Your code is perfectly safe, just invalid logically for what you want.

using the provided dispatch allows the doneWarmup to happen immediately during the processing of the first warmup, and fixes the bug.

The issue you're experiencing is being caused by your use of mainStore.dispatch inside of middleware.
Middleware has its own dispatch function available (as seen in the closure parameters) that you should be using instead, and indeed fixes the problem.

https://github.com/MichaelGuoXY/ReSwiftExample/blob/bdad901e2018ab87f4c6cb4683042e81e6f217f3/ReSwiftExample/ReSwift/ReSwift.swift#L46

Change mainStore.dispatch(AppAction.doneWarmup) to dispatch(AppAction.doneWarmup)

By dispatching again on mainStore you were adding the doneWarmup to the _END_ of your queue, which was already stacked with 3x warmup actions, and because it was processing those first, it was also dispatching 3x doneWarmup.

In fact, you don't have any invalid concurrency issues occurring, which is why the fatal error is not happening. Your code is perfectly safe, just invalid logically for what you want.

using the provided dispatch allows the doneWarmup to happen immediately during the processing of the first warmup, and fixes the bug.

Thanks, got it now.

So I guess it depends on the business logic whether to use dispatch or mainStore.dispatch, in other words, whether to immediately dispatch the action or append the action to the END of the dispatch queue. Please correct me if I am wrong.

Correct, @MichaelGuoXY. It is important one considers when the action will be processed when dispatching.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

CHOIMINSEOK picture CHOIMINSEOK  路  5Comments

nferruzzi picture nferruzzi  路  10Comments

dolanmiu picture dolanmiu  路  3Comments

Sethuraman picture Sethuraman  路  9Comments

Thurman1776- picture Thurman1776-  路  3Comments