coulix
coulix

Reputation: 3368

Simple jquery deferred queue

I have a method foo(param) that is synchronous.

I would like be free to call foo(param) without executing the actual foo() call until a particular event later in time.

It looks like a good candidate for deferred / promise but I must be doing something wrong since the timeout does not have any effect.

Should the log method return promise?

var log = function(message) {
    $('#log').append('<p>' + message + '</p>');
};

$(function() {
 
    var q = new $.Deferred();
    
    q.then(log("1"));
    q.then(log("2"));
    q.then(log("3"));
    q.then(log("4"));
    q.then(log("5"));
    q.then(log("6"));
    q.then(log("7"));
    
    setTimeout(function(){
        q.resolve();
    }, 10000);
    
    
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="log"></div>

Upvotes: 0

Views: 2604

Answers (3)

Roamer-1888
Roamer-1888

Reputation: 19288

Exactly what you should write depends entirely on what is meant by "until a particular event later".

The can be interpreted in two ways.

User Event: A user event (in a browser) is something like a 'click' or a 'mouseover'. It's occurrence is not determined by anything in javascript/jQuery and it may never happen. However, by attaching an event handler, you can ensure that if/when the event does happen, a predetermined course of action is taken.

The log sequence would be established as follows :

$(selector).on('eventType', function() {
    log("1");
    log("2");
    log("3");
    log("4");
    log("5");
    log("6");
    log("7");
});

Asycnchronous Response: An asynchronous response is an event that occurs when an asynchronous process completes. It's occurrence is determined by (a) the aync process being initiated and (b) its eventual completion. Again, completion may never happen but is more guaranteed than with a user event. A well written aync process should guarantee to complete (successfully or unsuccessfully).

The log sequence would be established as follows :

asyncProcess().then(function() {
    log("1");
    log("2");
    log("3");
    log("4");
    log("5");
    log("6");
    log("7");
});

As you can see, in both cases the log() statements are straightforward sequential lines of code.

It's unnecessary in either case to establish a Promise chain as log() is itself synchronous. However, if you want to explore the nature of Deferreds/Promises, then the asynchronous version could be written as follows :

asyncProcess()
    .then(log.bind(null, "1"))
    .then(log.bind(null, "2"))
    .then(log.bind(null, "3"))
    .then(log.bind(null, "4"))
    .then(log.bind(null, "5"))
    .then(log.bind(null, "6"))
    .then(log.bind(null, "7"));

// Note that .then() takes functions as arguments so `.bind(...)` is necessary to establish functions with bound-in arguments, without actually executing `log("1"); log("2"); log("3");` etc at the time the .then() chain is established.

As long as asyncProcess() returns a Promise that :

  • is already resolved
  • becomes resolved at some time in the future,

the functions specified in the .then() chain will execute and 1,2,3,4,5,6,7 will appear in your log.

In its simplest form, asyncProcess might look like this :

function asyncProcess() {
    return $.when(); //return a resolved promise
}

DEMO

To be slightly more adventurous, and to give the promise chain some purpose, you could explore its ability to convey data.

First, adapt asyncProcess() to return a promise resolved with a number passed as a parameter to the function :

function asyncProcess(n) {
    return $.when(n);//return a promise resolved with the valie 1
}

Then, adapt log() to accept a number n, log it, and return n+1:

function log(n) {
    $('#log').append('<p>' + n + '</p>');
    return n + 1;
}

And adapt the promise chain as follows :

asyncProcess(1)
    .then(log)
    .then(log)
    .then(log)
    .then(log)
    .then(log)
    .then(log)
    .then(log);

Here, the need for .bind() disappears as the raw log is used without any bound-in parameters. The parameter in each call of log is provided by the return value of the previous call.

DEMO

Upvotes: 0

user2010925
user2010925

Reputation:

When you do q.then(log("1")); it's attempting to get the function value of "log("1")" to pass to the then function. This is evaluating the function which is the reason why you're seeing them in the console instead of what you intend.

Wrapping them like so, should fix your problem:

q.then(function() {
    log("1")
});

Upvotes: 2

Andrew Magee
Andrew Magee

Reputation: 6684

Yes your lines that say q.then(log("...")) should say q.then(function() { log("...") }).

Your log function is fine but the way your code currently works is that you are already calling it and passing its return value to the then function. That's why you need to create a new function which the deferred will call later when it resolves.

Upvotes: 5

Related Questions