1

I'm wondering if there is a way to execute deferred task sequentially and conditionally. That is, i would like to perform some asynchronous task in sequence, where i would only execute the next one depending on the result of the preceding one. The result of the chain of promise should be the last to execute.

Any idea how to patch that up with Jquery deferred.

Edit:

Before i understood that ajax call properly, i started the following code for a function that verify that a value is not in the related value of another value.

function findBroaderOrNarrowerConceptInFieldSelectedList(field, value) {

    var intputfields = $("#instance_" + field + "_wrapper" + " .ds-authority-value")

    if (intputfields.length == 0)
        return undefined;

    //go over each input field and retreive the value

    for (var i = 0; i < intputfields.length; i++) {
        //check if our value is in the broader concepts of the input field concept
        //if true return the wrapper element, if false next steps
        if (is_broader_concept(value.id, $(intputfields[i]).attr('value'))) {

            var WrapperdivId = "#" + $(intputfields[i]).attr('name').replace("_authority", "") + "_wrapper";

            return $($(WrapperdivId)[0]);
        }
        //check if our value is in the narrower concepts of the input field concept
        //if true return the wrapper element
        if (is_narrower_concept(value.id, $(intputfields[i]).attr('value'))) {

            var WrapperdivId = "#" + $(intputfields[i]).attr('name').replace("_authority", "") + "_wrapper";

            return $($(WrapperdivId)[0]);
        }
    }

    return undefined
}

The problem is that my is_broader_concept look like this:

function is_broader_concept(broaderuri, concepturi) {

    return getBroaderConcepts(concepturi).then(function(data, textStatus, jqXHR ) {

        var broaders = data

        if (broaders.length == 0)
            return false;

        for (var i = 0; i < broaders.length; i++) {

            if (broaders[i].uri == broaderuri)
                 return true;
        }
    });
}

Now that i have learned about deferred and promise (not so new because i did it in scala (promise and future)), I would like to adapt my first method to work with deferred. However i don't see any construct that would help me achieve what i want easily.

EDIT2:

I have found the following lib which is excellent in case you do not matter launching many parallel task. Indeed it can return on the first that succeed. It is a modification of the When. Although i think it would be nice to not have to call all of them

https://github.com/terrycojones/jquery-when2

EDIT3:

I am more looking for a lib that would without bug at all do something like in https://codereview.stackexchange.com/questions/38420/sequential-function-call-in-javascript or Conditionals on a chained deferred in jquery

EDIT4: Update based on the response by @Roamer-1888

The proper is_broader and is narrower which works with his solution

function is_broader_concept_Promise(broaderuri, field) {

    var uri = field.value.substr(field.value.indexOf("http://"));

    return getBroaderConcepts(uri).then(function(data, textStatus, jqXHR ) {

        var broaders = data;

        if (broaders.length == 0)
            return $.Deferred( function( d){ d.reject(); }).promise();

        for (var i = 0; i < broaders.length; i++) {

            if (broaders[i].uri == broaderuri)
                return field;
        }
        return $.Deferred( function( d){ d.reject(); }).promise();
    });
}

function is_narrower_concept_Promise(narroweruri, field) {

    var uri = field.value.substr(field.value.indexOf("http://"));

    return getNarrowerConcepts(uri).then(function(data, textStatus, jqXHR ) {

        var narrowers = data;

        if (narrowers.length == 0)
            return $.Deferred( function( d){ d.reject(); }).promise();

        for (var i = 0; i < narrowers.length; i++) {

            if (narrowers[i].uri == narroweruri)
                return field;
        }
        return $.Deferred( function( d){ d.reject(); }).promise();
    });
}

Many thanks,

M

PS: I started jQuery about a month ago.

4
  • bring your example (or task) to the table Commented May 13, 2015 at 2:47
  • Where do the sequential tasks come in? How/where are you calling is_broader_concept()? Commented May 13, 2015 at 3:11
  • in the "for" inside of "findBroaderOrNarrowerConceptInFieldSelectedList" Commented May 13, 2015 at 3:12
  • "getBroaderConcepts(concepturi)" is where the async ajax call is done. Commented May 13, 2015 at 3:13

3 Answers 3

2

The .done() handler will only fire if the task succeeded, so you can use nested done() handlers like this:

step1().done(function() {
    step2().done(function() {
        step3().done(function() {
            // etc.
        });
    });
});

(where step1(), step2(), and step3() return deferred objects)

If you are making your own deferred objects, you can call deferred.resolve() to indicate that the task succeeded, or deferred.reject() to indicate that it failed.

See the jQuery.Deferred() documentation for more details.

Sign up to request clarification or add additional context in comments.

4 Comments

From what i red in the doc, done, install multiple callback that will be executed at once, because done, return the original object. So far the approach seems to be using "then"
They only execute at the same time if you chain them in series, i.e. .done().done(). If you use nested done calls, they should execute sequentially.
However, yes, you could also use then() in a similar way similar to how I am using done().
I'd suggest using .then() because it is the standard's implementation way of using promises. .done() is a non-standard jQuery implementation.
1

In plain language the objective can be summarised as follows :

"At each turn of a loop, if neither a broader-concept NOR a narrower-concept is discovered (asynchronously), then continue to test, otherwise break out of the loop".

In synchronous code this would be trivial, but in the asynchrous world there are some hoops to jump through. In particular, jQuery doesn't offer syntactic sugar for an asynchronous NOR. However, we can write one in the form of a custom jQuery.when_none() method :

jQuery.when_none = function() {
    //if all input-promises reject, then fulfil.
    //if any input-promise fulfills, then reject.
    var promises = $.map(arguments, function(p) {
        return $.Deferred(function(dfrd) {
            p.then(dfrd.reject, dfrd.resolve);//resolve/reject inversion
        }).promise();
    });
    return $.when.apply(null, promises);
};

Next, modify is_broader_concept() (and is_narrower_concept()) to return a promise which will be resolved/rejected instead of a promise which will resolve with a boolean true/false (also other minor changes) :

function is_broader_concept(uri, fld) {
    return getBroaderConcepts(fld.value).then(function(data) {
        if(data.indexOf(uri) > -1) {
            return fld; // The success state will be inverted by $.when_none(), and the rest of the .then() chain will be bypassed
        } else {
            return $.Deferred().reject().promise(); // The fail state will be inverted by $.when_none(), permitting the next step of the .then() chain.
        }
    });
}

And lastly, in findBroaderOrNarrowerConceptInFieldSelectedList() :

  • build a .then() chain by leveraging .reduce() to loop through the ".ds-authority-value" fields.
  • implement the required NOR logic with the cutom method $.when_none() to control the promise chain.
function findBroaderOrNarrowerConceptInFieldSelectedList(field, value) {
    return $("#instance_" + field + "_wrapper").find(".ds-authority-value").get().reduce(function(promise, fld) {
        return promise.then(function() {
            return $.when_none(
                is_broader_concept(value.id, fld),
                is_narrower_concept(value.id, fld)
            );
        });
    }, $.when()).then(function() {
        /* success: ie "not found", so reinvert the logic to force down the fail path */
        return $.Deferred().reject(value.id + ' is neither broader-concept nor narrower-concept.');
    }, function(fld) {
        /* fail: ie "found", so reinvert the logic to force down the success path */
        return $.when($('#' + fld.name.replace('_authority', '') + '_wrapper')); // $(fld) ???
    });
}

There's probably a simpler way to achieve the NOR logic but I can't think of it right now.

8 Comments

Actually the function is_broader_concept (is_narrower_concept) are not right. The logic is is I have A and B, and want to know is A is broader/narrower than B. I get the Narrower/Broader concept of B, and check if A is part of it.
In findBroaderOrNarrowerConceptInFieldSelectedList I do not undertands what you do with the reduce. Could you explain a bit more the logic in plain english. Maybe then i could understand the code. I need to understand the argument that are passed to function (promise, field). If i can now guess what field mean to you, i don't understand what promise is. I think in general i do not understand the function passed to reduce, and what argument reduce pass to that function. Maybe then i would understand better. Please, if you could clarify that it would help
Maat, yes, .reduce() is at the heart of this answer. The last example in the section "The Collection Kerfuffle" here explains it.
I have edited the code to avoid reusing field, which was confusing. Now, fld is equivalent to your origainal intputfields[i] and fld.value is equivalent to $(intputfields[i]).attr('value').
I tried to implement a corrected version of your broader/narrower logic in the question, but may have got it wrong. It is certainly different from your A/B description above and I'm not sure how to correct it.
|
0

When-JS would do the trick just right. That is what i was looking for

https://github.com/cujojs/when/blob/master/docs/api.md#api

3 Comments

Maat, could you say more please. How did when.js help? Can we see your final code please?
So far i only only see how to perform it as form of race. Whenever one result is true, return true. Otherwise, iterate seem tricky to use. I'm working on it.
Yes, even with when.js it's not completely straightforward. I will post a jQuery solution, which may help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.