Ad

Mix Of Synchronous Functions And Asynchronous Deferred JQuery Objects

I want to send Ajax request and perform some unrelated actions while data arrived. After finishing actions I like to wait for Ajax finishing and perform another actions.

To be concrete lets see to stripped example:

$.ajax({url: "/reqA"})
    .done(updateA)
    .done($.ajax("/reqB").done(updateB))
    .done(updateFinal)

updateFinal should be performed after completion of synchronous updateA and asynchronous /reqB and following synchronous updateB.

Above code is wrong because all subsequent .done() operates on promise from /regA and race condition occur between updateB and updateFinal.

I can fix code with .then:

$.ajax({url: "/reqA"})
    .done(updateA)
    .then($.ajax("/reqB").done(updateB))
    .done(updateFinal)

But next I want to run updateA after sending request /reqB (because JS engine single threaded and updateA execution blocked asynchronous process /reqB!!).

Following code doesn't work:

$.ajax({url: "/reqA"})
    .then($.ajax("/reqB").done(updateB))
    .done(updateA)
    .done(updateFinal)

because updateA execution will be delayed to updateB execution!!

I think that problem can be solved with $.when function, but updateA isn't promise and I don't see order of execution guaranty in official $.when docs. It could look like:

$.ajax({url: "/reqA"})
    .then(
         $.when(
              $.ajax("/reqB").done(updateB),
              fakeResolvedPromiseWrapperAroundSyncFunc(updateA)
         )
    ).done(updateFinal)

Are there any standard fakeResolvedPromiseWrapperAroundSyncFunc solution in jQuery library?

Any other path to run synchronous code after starting asynchronous and later joining to result of async call?

Ad

Answer

In your .done() and .then() handlers, you need to pass function REFERENCES, not just call the functions in the parens in order to be able to control the timing of the execution.

In a promise chain, you can pass either a synchronous function or an asynchronous function as a .then() handler. If you pass an asynchronous function, it just needs to return a promise and that promise will be inserted into the promise chain at that point. The synchronous function will just be called when it gets its turn and then the next step in the chain will go right after it (since it's synchronous).

For example, change this:

$.ajax({url: "/reqA"})
    .done(updateA)
    .done($.ajax("/reqB").done(updateB))
    .done(updateFinal)

to this:

$.ajax({url: "/reqA"})
    .then(updateA)
    .then(function() {
         return $.ajax("/reqB").then(updateB)
     }).then(updateFinal);

Or, this could be written as:

$.ajax({url: "/reqA"})
    .then(updateA)
    .then(function() {
         return $.ajax("/reqB");
     }).then(updateB)
     .then(updateFinal);

This will execute the ajax function and when the Ajax call completes, it will execute updateA(). After the synchronous updateA() completes, then it will call the anonymous function that then executes the /reqB ajax call. When that ajax call completes, it will execute updateB(). When updateB() is done, then updateFinal() will get called.

And, change this:

$.ajax({url: "/reqA"})
    .then($.ajax("/reqB").done(updateB))
    .done(updateA)
    .done(updateFinal)

to this:

$.ajax({url: "/reqA"})
    .then(function() {
        return $.ajax("/reqB").then(updateB);
     }).then(updateA)
    .then(updateFinal)

When you directly execute the function inside the parens like you were doing, it is called IMMEDIATELY and the return result from executing the function is what is passed as the .then() handler. When you pass a function reference, the .then() infrastructure can then call that function sometime later.


Your use of $.when() could also work. It is used when you want to execute multiple items in parallel and want to know when they are all done. The code recommendations above do serialized execution (one thing after another).


As for mixing synchronous and asynchronous code, that can work perfectly fine. The first operation in the sequence must create a promise which you can chain a .then() handler from. After that, the callback for any given .then() handler can do any of the following:

  1. It can be synchronous and return a regular value. This value becomes the resolved value of the promise chain and then subsequent .then() handlers are invoked passing that value to them.

  2. It can be synchronous and not return a value. The promise chain has a value of undefined at that point and the next .then() handler in the chain will be invoked.

  3. It can be synchronous and throw an exception. The exception becomes the rejected value of the current promise and any attached .then() reject handlers, not resolve handlers are called.

  4. It can be asynchronous and return a promise. This new promise is inserted into the chain and subsequent .then() handlers are not called until this new promise is fulfilled. Whether the subsequent resolve or reject handlers are called will depend upon whether this new promise resolves or rejects.

  5. It can be asynchronous and not return anything or return a regular (non-promise) value. In this case, a new asynchronous operation is started, but it has no effect on the current promise chain because no promise related to the new asynchronous operation was returned. This new asynchronous operation just runs on its own at its own pace and when it finishes has no effect on the current promise chain. Since no promise is returned from the .then() handler, the current promise proceeds as if this was a synchronous handler as describe

Ad
source: stackoverflow.com
Ad