locked
Async operation with promise is blocking UI thread RRS feed

  • Question

  • My homepage has to load a lot of stuff so I have an animated gif in a dive that just spins around...  when the loading is done (which happens in a promise), I hide the spinner and show the content...

    It works except the gif never spins and the UI thread looks frozen...  I thought running code in a promise takes it off the UI thread?  What am I doing wrong?


    www.emadibrahim.com

    Tuesday, November 27, 2012 10:00 PM

Answers

  • Yes, creating a promise doesn't make the code async. It just says to execute the code when you fulfill the promise.

    For #1, here's the code from my book. First, some worker code in a file worker_count.js:

    onmessage = function (e) {
        switch (e.data.method) {
            case "countFromZero":
                countFromZero(e.data.max, e.data.increment);
                break;
    
            default:
                break;
        }
    };
    
    function countFromZero(max, increment) {
        var sum = 0;
        max = 10;
    
        for (var x = 0; x < max; x += increment) {
            sum += x;
        }
    
        postMessage({ method: "countFromZero", sum: sum });
    }
    

    The the app code that invokes it:

    var worker = new Worker('worker_count.js');
    worker.onmessage = function (e) { //e.data.sum is the result }
    
    //Call the method 
    worker.postMessage({ method: "countFromZero", max: 1000, increment: .00005 });
    

    Note that postMessage is effectively an async function. In the code above, the countFromZero method gets invoked, and the worker.onmessage handler will be called when the worker finishes the job and call postMessage itself.

    Here, then, is app code that wraps a promise around the worker:

    // This is the function variable we're wiring up.
    var workerCompleteDispatcher = null;
    
    var promiseJS = new WinJS.Promise(function (completeDispatcher, errorDispatcher,
        progressDispatcher) {
        workerCompleteDispatcher = completeDispatcher;
    });
    
    // Worker would be created here and stored in the 'worker' variable
    
    // Listen for worker events
    worker.onmessage = function (e) {
        if (workerCompleteDispatcher != null) {
            workerCompleteDispatcher(e.data.sum);
        }
    }
    
    promiseJS.done(function (sum) {
        // Output for JS worker
    });
    

    Chapter 16 has a couple pages of text to explan how all this works in detail.

    Something to notice is that the function argument passed to the WinJS.Promise constructor is one that receives dispatcher functions for completed, error, and progress. These are functions that will call whatever handlers have been given to the promise in its then/done methods. So in my code above, I save that function in workerCompleteDispatcher so that when I get the message from the worker, I can invoke whatever completed handlers are associated with the promise. In this case it's the anonymous function(sum) given to promise.JS.done, and you can see that I simply give the result to the dispatcher that then calls that completed handler on my behalf.

     .Kraig
    • Marked as answer by Emad Wednesday, November 28, 2012 8:16 PM
    Wednesday, November 28, 2012 12:48 AM

All replies

  • Actually, a promise in and of itself says nothing about how the code runs. If you take a block of code and wrap it with WinJS.Promise.as or such, you're just saying that the code will execute when you call then/done, but all that happens on the UI thread.

    Async behavior--that is, running code on another thread--happens through three means:

    1. A JavaScript worker (web worker), which you can wrap in a promise (new WinJS.Promise to handle the worker messages) in order to chain/join the async operation to others.
    2. An async method written in a C#/VB WinRT component where the code uses the Task class.
    3. An async method written in a C++ WinRT component where the code uses the concurrency library.

    In the latter two cases, async methods (including those from the built-in WinRT APIs) happen to be projected into JavaScript as promises; in #1 you use new WinJS.Promise to do the same manually. But in every case, it's not the promise that runs code on another thread--it's purpose is just to manage completed/error/progress handlers. It's the underlying worker or async method that runs code on another thread.

    Creating async APIs in WinRT components as well as with web workers is something I cover in Chapter 16 of my book, in the section "Implementing Async Methods,"  if you want all the details.

    Kraig

    Author, Programming Windows 8 Apps with HTML, CSS, and JavaScript, a free ebook from Microsoft Press


    Wednesday, November 28, 2012 12:13 AM
  • Awesome...  do you have an example of how to do it in #1?

    You are basically saying this code runs on the UI thread - correct?

    return new WinJS.Promise(function (complete) {
       //blocking code i.e db/file/network access
    });


    www.emadibrahim.com

    Wednesday, November 28, 2012 12:33 AM
  • Yes, creating a promise doesn't make the code async. It just says to execute the code when you fulfill the promise.

    For #1, here's the code from my book. First, some worker code in a file worker_count.js:

    onmessage = function (e) {
        switch (e.data.method) {
            case "countFromZero":
                countFromZero(e.data.max, e.data.increment);
                break;
    
            default:
                break;
        }
    };
    
    function countFromZero(max, increment) {
        var sum = 0;
        max = 10;
    
        for (var x = 0; x < max; x += increment) {
            sum += x;
        }
    
        postMessage({ method: "countFromZero", sum: sum });
    }
    

    The the app code that invokes it:

    var worker = new Worker('worker_count.js');
    worker.onmessage = function (e) { //e.data.sum is the result }
    
    //Call the method 
    worker.postMessage({ method: "countFromZero", max: 1000, increment: .00005 });
    

    Note that postMessage is effectively an async function. In the code above, the countFromZero method gets invoked, and the worker.onmessage handler will be called when the worker finishes the job and call postMessage itself.

    Here, then, is app code that wraps a promise around the worker:

    // This is the function variable we're wiring up.
    var workerCompleteDispatcher = null;
    
    var promiseJS = new WinJS.Promise(function (completeDispatcher, errorDispatcher,
        progressDispatcher) {
        workerCompleteDispatcher = completeDispatcher;
    });
    
    // Worker would be created here and stored in the 'worker' variable
    
    // Listen for worker events
    worker.onmessage = function (e) {
        if (workerCompleteDispatcher != null) {
            workerCompleteDispatcher(e.data.sum);
        }
    }
    
    promiseJS.done(function (sum) {
        // Output for JS worker
    });
    

    Chapter 16 has a couple pages of text to explan how all this works in detail.

    Something to notice is that the function argument passed to the WinJS.Promise constructor is one that receives dispatcher functions for completed, error, and progress. These are functions that will call whatever handlers have been given to the promise in its then/done methods. So in my code above, I save that function in workerCompleteDispatcher so that when I get the message from the worker, I can invoke whatever completed handlers are associated with the promise. In this case it's the anonymous function(sum) given to promise.JS.done, and you can see that I simply give the result to the dispatcher that then calls that completed handler on my behalf.

     .Kraig
    • Marked as answer by Emad Wednesday, November 28, 2012 8:16 PM
    Wednesday, November 28, 2012 12:48 AM