Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to control flow of promises in linear call order?

I still don't have tight grip on promises. Say I have a code:

selected.eq(i)   // blink
    .fadeOut( 200 ).delay( 50 ).fadeIn( 400 )
    .delay( 100 )
    .fadeOut( 200 ).delay( 50 ).fadeIn( 400 );

and later I call:

selected.unbind('click')
    .promise().done(function ()
        {
            selected.fadeOut(500);
        });

It works as excepted -- once the blinking is done, the last fadeOut kicks in. But when I do the blinking via plugin (with setup to use jQuery animation) so my entire first part is just one call:

selected.eq(i).modernBlink({ 
    duration: 750,
    iterationCount: 3,
    auto: true}
    );

the body of blink is:

ModernBlink.prototype._fallbackAnimation = function _fallbackAnimation( iterationCount ) {
    var self = this,
    duration = this.options.duration / 2;

    if ( iterationCount > 0 || iterationCount === 'infinite' ) {
        iterationCount = iterationCount === "infinite" ? "infinite" : iterationCount - 1;

    this.el.animate( { 'opacity': 0 }, duration ).promise().done( function() {
        self.el.animate( { 'opacity': 1 }, duration );
        self._fallbackAnimation( iterationCount );
    });
}
};

so it is recursive call based on promise as well. The outcome is different -- after first iteration from blink my code kicks in, because my promise won.

The visual effect is -- blink, fade out (mine part), keep blinking.

My question is -- how to tell jQuery the blink promises are more important than mine?

NOTE: I cannot attach directly the second part to the first part, because they are scattered in code, and sometimes the first part is not executed.

The code of blink comes from Modern Blink by leonderijke. MB was used here as example!

UPDATE: Big picture, I don't know if this helpful or not, but anyway:

if (Environment.Mode=='blink') // my private settings variable
    call_blink(); // part 1, presented above
call_fade_out(); // part 2, presented above

This is why, I cannot chain them. I have similar "configuration" of code used some more, so I would like to understand it, and use it here and in other places.

like image 399
greenoldman Avatar asked Oct 21 '22 00:10

greenoldman


2 Answers

Try utilizing the animationend event

$.each(["webkit", "moz", "MS", "o", ""], function (k, v) {
    selected[0].addEventListener(v !== "" ? v + "AnimationEnd" : "animationend"
    , function (e) {
        $(this).unbind('click')
            .promise().done(function (el) {
            el.fadeOut(500);
            console.log("done")
        });
    })
})

jsfiddle http://jsfiddle.net/guest271314/x7gqb1g4/


An alternative approach ; "infinite" loop by maintaining count !== 0 , "stop" by calling .stop() , clearQueue() setting .data() flag

    // v2
    // `d` : duration , `count` : iteration
    (function ($) {
    $.fn.blink = blink;
    function blink(d, count) {
        var el = $(this);
        $.fx.interval = 0;
        return el.each(function (i, elem) {
            var elem = $(elem);
            elem.data("count", count);
            return elem.fadeTo(d, "0", "linear", function () {
                elem.fadeTo(d, "1", "linear", function () {
                    elem.data("count", --count);
                    return (elem.data("count") !== 0 && !elem.data("stop") 
                           ? elem.blink(d, elem.data("count")) 
                           : elem.stop(true, true).data("stop", false))
                })
            })
        }).promise("fx")
    };
}(jQuery));

// e.g.,
var selected = $("div")
, button = $("button:first")
, stop = $("button:last");

selected.on("click", function (e) {
    // start `$.fn.blink` , log returned `promise` on stop
    $(this).blink(750, 10).then(function (el) {
        console.log(el, el.queue(), el.data());
    })
});

button.on("click", function () {
    // unbind `click` event
    selected.unbind('click')
    .promise().then(function (el) {
        el.fadeOut(500);
    });
});

stop.on("click", function () {
    // stop animation
    selected.data("count", null).data("stop", true).clearQueue()
})

jsfiddle http://jsfiddle.net/guest271314/33ptL9do/

like image 139
guest271314 Avatar answered Oct 22 '22 14:10

guest271314


You will need to make modernBlink return a promise that represents the result you are after - the end of the blinking. It simply is impossible to intercept animations that will only be chained in the future. The plugin you're using should return a promise, or at least provide a callback; if it doesn't to that you'll need to use a different one or fork it.

In your case, the blink method will need to repeatedly chain itself to the promise by using then:

ModernBlink.prototype._fallbackAnimation = function(iterationCount) {
    var self = this,
    duration = this.options.duration / 2;

    if (iterationCount === 'infinite' || iterationCount-- > 0) {
        return this.el.animate({'opacity': 0}, duration).promise()
        .then(function() {
            return self.el.animate({'opacity': 1}, duration).promise();
        })
        .then(function() {
            return self._fallbackAnimation(iterationCount);
        });
    } else {
        return this;
    }
};

Now, your big picture might look like this:

if (Environment.Mode=='blink')
    var step1 = new ModernBlink()._fallbackAnimation(5);
else
    var step1 = jQuery.when(undefined);

var step2 = step1.then(call_fade_out);
like image 31
Bergi Avatar answered Oct 22 '22 13:10

Bergi