11

Sometimes the user starts an extended technical operation that takes a while to execute. In these cases, it's usually nice to display some kind of progress bar, along with information about which task is being executed right now.

In order to avoid close-coupling the UI and the logic layers, it's usually best to have the communication occur via some kind of proxy. That is, the back-end should not be manipulating its own UI elements, or even interacting with the intermediary layer directly.

Obviously, there has to be some call-back somewhere to make this work. I've generally implemented it in one of two ways:

  1. Pass a mutable object to the back-end, and have the back-end make changes to it on progress. The object notifies the front-end when a change occurs.

  2. Pass a call-back function of the form void f(ProgressObject) or ProgressObject -> unit that the back-end invokes. In this case, the back-end constructs the ProgressObject and it's completely passive. It must construct a new object every time it wants to report progress, I assume.

What are the drawbacks and advantages of these methods? Is there an agreed-upon best method to use? Are there different circumstances for their use?

Are there completely different techniques of reporting progress that I've overlooked?

  • 1
    Regarding mutable vs immutable, the advantages and drawbacks are the same as they are anywhere else. Regarding the progress object, this can be very light; it can be as simple as a single number: a percentage. – Robert Harvey Dec 18 '15 at 15:50
  • @RobertHarvey The size of the progress object usually depends on the UI requirements. Look at the Windows copy dialog for example. I imagine it requires a lot of information. – GregRos Dec 18 '15 at 15:52
  • 1
    @RobertHarvey That's news to me. What is it? – GregRos Dec 18 '15 at 16:07
  • 1
    I'll bite. We use BackgroundWorker that R.H. mentions. Wrapped in a custom class along with a "progress form", etc. and a simple mechanism for communicating an exception - as BackgroundWorker by design runs in a separate thread. To the extent we use its features in a way suggested by .Net then that could be said to be idiomatic. And in any given language/framework context "idiomatic" may be best. – radarbob Dec 18 '15 at 17:31
  • 2
    I do not see any significant differences between your two methods. An object passed from the front end to the backend which offers methods which lead to a notification of the front end has actually the function of a callback. And if your second approach uses a more or less complex parameter object for passing information, or if it uses a few simple values does not make a difference from an architectural point of view. In both approaches the backend actively informs the frontend, the differences are just minor details, so there is no different concept described here. – Doc Brown Dec 18 '15 at 22:43
8

Pass a mutable object to the back-end, and have the back-end make changes to it on progress. The object notifies the front-end when a change occurs.

It's difficult to balance efficiency if the backend notifies in this respect. Without care you might find that incrementing your progress ends up doubling or tripling the time it takes to complete the operation if you're aiming for a very smooth progress update.

Pass a call-back function of the form void f(ProgressObject) or ProgressObject -> unit that the back-end invokes. In this case, the back-end constructs the ProgressObject and it's completely passive. It must construct a new object every time it wants to report progress, I assume.

I don't get the difference here so much.

Are there completely different techniques of reporting progress that I've overlooked?

Poll from the front-end in a separate thread with atomic increments in the backend. Polling makes sense here since it's for an operation that finishes in a finite period and the likelihood of the frontend picking up state changes is high, especially if you're aiming for a silky smooth progress bar. You could consider condition variables if you don't like the idea of polling from the frontend thread, but in that case you might want to avoid notifying on every single granular progress bar increment.

2

This is the difference between a push and pull notification mechanism.

The mutable object (the pull) will need to be repeatably polled by the UI and synchronized if you expect the back-end task to be executed in a background/worker thread.

The callback (the push) will only create work for the UI when something actually changes. Many UI frameworks also have a invokeOnUIThread callable from a worker thread to make a piece of code run on the UI thread so you can actually make the changes without treading in to thread-related hazards. (pun intended)

In general push notifications are preferable because they only make work when work needs to be done.

  • 1
    I think what you say is right in general. However, for this particular case, a progress bar, changes may occur rapidly. If your expectation is that "progress" is likely to change many times a second, it makes more sense to use the pull model as otherwise you have to worry about the UI getting too many notifications to handle. – Gort the Robot Dec 18 '15 at 15:57
  • Sending down a progress object can obscure the notification mechanism you're using from the back-end, since maybe the progress object is doing the call-backs. I've actually never used a pull mechanism as far as I remember, and I sort of forgot about it :P – GregRos Dec 18 '15 at 15:59
  • The mutable object (the pull) will need to be repeatably polled by the UI and synchronized if you expect the back-end task to be executed in a background/worker thread. -- Not if the mutable object is the dialog itself, or a working interface to it. Of course, that amounts to a callback anyway. – Robert Harvey Dec 18 '15 at 16:02
  • 1
    Huh? The OP clearly describes two different forms of a push mechanism, in none is a polling necessary. – Doc Brown Dec 18 '15 at 17:59
0

I'm using websockets with AngularJS. When the front end receives a message it displays it in a designated message area which fades to blank after a few seconds. On the back end I just post status messages to a message queue. I only send text, but there's no reason I couldn't send a status object with values like percentage complete or transfer speed.

0

You mention your "two ways" as if they are separate concepts but I want to push back on that a bit.

  1. Pass a mutable object to the back-end, and have the back-end make changes to it on progress. The object notifies the front-end when a change occurs.

You have already said that you want to avoid close-coupling the UI and the logic, so I can safely assume that this "mutable object" that you are passing is actually an implementation of a particular interface which is defined in the logic module. As such, this is merely another way of passing a callback into the process which is periodically calls with information about progress.

As for benefits and drawbacks...

A drawback for method (1) is that the class that implements the interface can only do it once. (If you want to perform different jobs with different invocations, you will need a switch statement or the visitor pattern.) With method (2), the same object can use a different callback for every invocation of the backend code without the need for a switch.

A strength for method (1) is that it is far easier to have multiple methods on the interface than to deal with method (2)'s multiple callbacks or single callback with a switch statement for multiple contexts.

-2

The techniques you can use may be very differt.

I try to figure out with different scenario

  • Request to db
  • download file

A simple login request to db (mean respond from db with one elemt) don't need a report progress but it alwasy can fire off UI thread in separate task ex. async or backgroundworker, here you just need one callback for result.

But what if you query for see all your inventory with 1mln item? This query should take several minutes to complete, so in this case you need to implement your perport progress in your business logic in form item / items, then you can update your UI and may be give the option cancel callback.

Same case for file download. You can always implement here your progress callback in form byte of bytes, and hold all comunication control over http is very commonly pattern.

On my personally approach i implement my business progress logic only to my clients, avoiding to share other object with end-point.

  • 1
    This does not really answer the question about advantages/disadvantages. – Benni Dec 19 '15 at 16:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.