Results 1 to 3 of 3

Thread: [RESOLVED] How to wait for an await method to cancel?

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    May 2002
    Posts
    1,602

    Resolved [RESOLVED] How to wait for an await method to cancel?

    Hi!

    I have an application where the user can scroll through different menu options (like a carousel). And for each of these menu options, different data is to be loaded in a content view.

    If the user scrolls really slow, and wait at each carousel option, there is a text says "loading data..." and when the data has been loaded the info is displayed, nice and easy.

    The code for this is quite easy, in pseudo it looks like inside the "carousel_changed" eventhandler


    Data.Clear() // This clears the result ObservableCollection

    hide result grid

    show loading data...

    start async call like

    res = await LoadData(carouselId)

    BindData(res)


    BindData is a simple method that is adding the results to the list called Data.
    The problem is when the user scrolls really fast through the carousel. Then all wierd stuff starts to happen in the UI, it becomes out of sync and the data is getting mixed up.

    I tried implementing a solution with CancellationToken that is like this inside the "carousel_changed" event handler

    check if data is loading by checking an IsLoading boolean

    if data is loading, set the cancellationTolenSource.Cancel()

    catch OperationCancelledException and reset the token.

    The problem with the above is that there is time passing between when I call the Cancel() method on the tokenSource to when the actual Exception is caught which would indicate that the async operation was cancelled. This is due to the fact that I am calling multiple REST services and one of them might take 1-2 seconds to finish. And after each call I check

    await call1

    if(token.IsCAncellationRequested) then
    token.ThrowIfCancellationRequested()
    end if

    await call2

    if(token.IsCAncellationRequested) then
    token.ThrowIfCancellationRequested()
    end if

    await call3

    etc..

    Maybe there is a better way? Lots of code...


    And by that time, the await LoadData() method is called once again and aborted because the token is still in "Cancelled" state... and this just messes up things further.

    I want some way to wait for the cancel operation to abort without locking up the UI thread. And also in the "carousel_changed" wait say 400 ms before actually start loading stuff, to prevent too many operations as a result of the user quickly scrolling through the carousel.

    I have spent hours and hours of googling, and haven't found anything that would work.

    Please help me get in sync!!! Any small example of code that would solve my scenario would be extremly useful.

    /Henrik

  2. #2
    Frenzied Member IanRyder's Avatar
    Join Date
    Jan 2013
    Location
    Healing, UK
    Posts
    1,232

    Re: How to wait for an await method to cancel?

    Hi,

    I have faced a similar problem in the past and I solved this by thinking about this a bit differently. I found that trying to wait for the cancellation of a background thread could take a variable amount of time depending on the data being loaded and I always ended up duplicating data and messing up the UI similar to what you are experiencing now.

    To get round this, and to let the User scroll through options as fast as they wanted to, I decided that I needed to let the Background Thread finish its current work before trying to get it to do something else. The trick here was to make sure that the UI Thread continued to keep track of what the user was doing, doing minimal work on the UI Thread, and then only processing the LAST Selected Option once the Background Thread became available again. I achieved this by adding each selected user option to a Stack and then once the Background Thread became available the LAST item on the Stack was retrieved and processed on the background thread.

    Here’s a quick example using a bunch of TextBox’s on a form. Just run the cursor over the TextBox’s as quick as you want and you will see that only the last textbox is processed when the Backgoundworker becomes free. Maybe you can adapt this idea to your Carousel problem.

    vb.net Code:
    1. Public Class Form1
    2.   Private carouselStack As New Stack(Of String)
    3.  
    4.   Private Sub Button1_Enter(sender As Object, e As EventArgs) Handles Button1.MouseEnter, Button2.MouseEnter, _
    5.     Button3.MouseEnter, Button4.MouseEnter, Button5.MouseEnter, Button6.MouseEnter, Button7.MouseEnter
    6.  
    7.     carouselStack.Push(sender.name)
    8.     If Not BackgroundWorker1.IsBusy Then
    9.       BackgroundWorker1.RunWorkerAsync()
    10.     End If
    11.   End Sub
    12.  
    13.   Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    14.     'do some labour intensive work
    15.     Dim lastCarouselSelection As String = carouselStack.Pop
    16.     carouselStack.Clear()
    17.     Label1.Invoke(Sub()
    18.                     Label1.Text = String.Format("Processing Button {0}", lastCarouselSelection)
    19.                   End Sub)
    20.     Threading.Thread.Sleep(2000)
    21.   End Sub
    22.  
    23.   Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
    24.     If carouselStack.Count > 0 Then
    25.       BackgroundWorker1.RunWorkerAsync()
    26.     End If
    27.   End Sub
    28. End Class

    Hope that helps.

    Cheers,

    Ian

  3. #3

    Thread Starter
    Frenzied Member
    Join Date
    May 2002
    Posts
    1,602

    Re: How to wait for an await method to cancel?

    Quote Originally Posted by IanRyder View Post
    Hi,

    I have faced a similar problem in the past and I solved this by thinking about this a bit differently. I found that trying to wait for the cancellation of a background thread could take a variable amount of time depending on the data being loaded and I always ended up duplicating data and messing up the UI similar to what you are experiencing now.

    To get round this, and to let the User scroll through options as fast as they wanted to, I decided that I needed to let the Background Thread finish its current work before trying to get it to do something else. The trick here was to make sure that the UI Thread continued to keep track of what the user was doing, doing minimal work on the UI Thread, and then only processing the LAST Selected Option once the Background Thread became available again. I achieved this by adding each selected user option to a Stack and then once the Background Thread became available the LAST item on the Stack was retrieved and processed on the background thread.

    Here’s a quick example using a bunch of TextBox’s on a form. Just run the cursor over the TextBox’s as quick as you want and you will see that only the last textbox is processed when the Backgoundworker becomes free. Maybe you can adapt this idea to your Carousel problem.

    vb.net Code:
    1. Public Class Form1
    2.   Private carouselStack As New Stack(Of String)
    3.  
    4.   Private Sub Button1_Enter(sender As Object, e As EventArgs) Handles Button1.MouseEnter, Button2.MouseEnter, _
    5.     Button3.MouseEnter, Button4.MouseEnter, Button5.MouseEnter, Button6.MouseEnter, Button7.MouseEnter
    6.  
    7.     carouselStack.Push(sender.name)
    8.     If Not BackgroundWorker1.IsBusy Then
    9.       BackgroundWorker1.RunWorkerAsync()
    10.     End If
    11.   End Sub
    12.  
    13.   Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    14.     'do some labour intensive work
    15.     Dim lastCarouselSelection As String = carouselStack.Pop
    16.     carouselStack.Clear()
    17.     Label1.Invoke(Sub()
    18.                     Label1.Text = String.Format("Processing Button {0}", lastCarouselSelection)
    19.                   End Sub)
    20.     Threading.Thread.Sleep(2000)
    21.   End Sub
    22.  
    23.   Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
    24.     If carouselStack.Count > 0 Then
    25.       BackgroundWorker1.RunWorkerAsync()
    26.     End If
    27.   End Sub
    28. End Class

    Hope that helps.

    Cheers,

    Ian

    Thanks for the reply.

    Yeah, I ended up doing something similar to your idea, but instead of queuing up all calls, in the carousel_changed event handler I check if work is in progress AND if there is a cancellation in progress. If that is the case I fire up a while(true) loop (with a 200 ms delay between each iteration)in a non UI thread that simply catches all calls, and I exit that loop only when the taskcancelledexception is fired meaning that the work has been terminated. Only then do I exit the while loop and keep doing the work. The "selectedIndex" property, which I use in the work method, is updated when the user scroll through the carousel since it is done on the UI thread, Everything is smooth as silk and I don't get any wierd binding problems.

    It is funny, I thought there was a "by the book" way to solve this, but since we came up with two very different ways to solve it, by async/await + a while loop or in your case with a stack, I guess the Task-library has nothing ready for us. Using Task.Wait is quite useless since it is old style Task programming and it may cause deadlocks if not handled with special care in combination with .ContinueWith() which was very popular.

    Carousels and pivots are aspecial case, normally in standard desktop apps I just disable the "work"-button and enables it once the task is cancelled. But in Modern UI and Phone 8.1 apps, the pivots and carousels call for special attention.

    cheers!
    Henrik

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width