.each()完成后调用jQuery函数


183

在jQuery中,调用(或任何其他类型的迭代回调)完成后,可以调用回调触发事件.each()

例如,我要完成此“淡入淡出”操作

$(parentSelect).nextAll().fadeOut(200, function() {
    $(this).remove();
});

在进行一些计算之前,在。之后插入元素$(parentSelect)。如果现有元素仍然对jQuery可见,并且睡眠/延迟一些任意时间(每个元素200个)似乎最好是一种脆弱的解决方案,那么我的计算是不正确的。

我可以轻松地.bind()对事件回调进行必要的逻辑处理,但是我不确定.trigger()在上述迭代完成后如何干净地调用。显然,我无法在迭代内调用触发器,因为它将触发多次。

在的情况下$.each(),我曾考虑过在data参数的末尾添加一些内容(我会在迭代主体中手动查找),但是我不想被迫这样做,所以我希望还有其他一些优雅之处控制有关迭代回调的流程的方法。


1
我是否正确地理解,不是您要完成的仅仅是“ .each()”本身,而是要由“ .each()”调用启动的所有动画?
Pointy 2010年

这是一个好点。对于这个特定的问题,是的,我主要关注“ .each()”本身的完成……但是我认为您提出了另一个可行的问题。
路德·贝克

Answers:


161

@tv的答案的替代方法:

var elems = $(parentSelect).nextAll(), count = elems.length;

elems.each( function(i) {
  $(this).fadeOut(200, function() { 
    $(this).remove(); 
    if (!--count) doMyThing();
  });
});

请注意,.each()它本身是同步的 —调用to .each()之后的语句仅在.each()调用完成之后才执行。但是,在迭代中开始的异步操作.each()当然会以自己的方式继续。这就是这里的问题:淡化元素的调用是由计时器驱动的动画,并且这些动画以自己的速度继续进行。

因此,上面的解决方案可以跟踪淡出了多少个元素。每次调用.fadeOut()都会获得完成回调。当回调函数注意到所有涉及的原始元素都计入了回调时,可以放心地采取所有后续操作,以确保所有衰落都已完成。

这是一个有四年历史的答案(目前为2014年)。一种现代的方法可能涉及使用Deferred / Promise机制,尽管以上操作很简单并且应该可以正常工作。


4
我喜欢此更改,尽管由于要递减计数,所以我将比较结果设为0而不是逻辑否定(--count == 0)。对我而言,尽管具有相同的效果,但它使意图更清晰。
tvanfosson

事实证明,您对问题的最初评论是当场的。我当时在问.each(),以为那是我想要的,但是正如您和tvanfosson以及现在patrick所指出的那样-这是我真正感兴趣的最后淡出。我想我们都同意您的示例(代替计数索引)可能是最安全的。
路德·贝克

@ A.DIMO是什么意思,“太复杂了”?哪一部分比较复杂?
尖尖的2014年


156

好的,这可能要花一点时间,但是.promise()也应该实现您所追求的。

承诺文档

我正在从事的项目的示例:

$( '.panel' )
    .fadeOut( 'slow')
    .promise()
    .done( function() {
        $( '#' + target_panel ).fadeIn( 'slow', function() {});
    });

:)


7
.promise()在.each()上不可用
Michael Fever

25

JavaScript是同步运行的,因此您放置的任何内容在完成each()之前都不会运行each()

考虑以下测试:

var count = 0;
var array = [];

// populate an array with 1,000,000 entries
for(var i = 0; i < 1000000; i++) {
    array.push(i);
}

// use each to iterate over the array, incrementing count each time
$.each(array, function() {
    count++
});

// the alert won't get called until the 'each' is done
//      as evidenced by the value of count
alert(count);

调用警报时,计数将等于1000000,因为直到each()完成警报才会运行。


8
在给定示例中的问题是,fadeOut被置于动画队列中,并且无法同步执行。如果您使用each和分别安排每个动画,则仍然必须在动画之后触发事件,而不是每个动画都结束。
tvanfosson

3
@tvanfosson-是的,我知道。根据路德希望实现的目标,您的解决方案(以及Pointy的解决方案)似乎是正确的方法。但是从Luther的评论中…… 天真地,我正在寻找elems.each(foo,bar)之类的东西,其中foo ='函数应用于每个元素',而bar ='在所有迭代完成后回调'。...他似乎坚持认为这是一个each()问题。这就是为什么我把这个答案混在一起。
user113716 2010年

你是正确的帕特里克。我确实(mis)理解了.each()正在调度或排队我的回调。我认为调用fadeOut间接导致了我的结论。tvanfosson和Pointy都提供了关于fadeOut问题的答案(这是我真正想要的),但是您的帖子实际上在某种程度上纠正了我的理解。正如Pointy和tvanfosson回答我试图提出的问题时,您的答案实际上最好地回答了最初提出的问题。我希望我能选择两个答案。感谢您“将答案综合起来” :)
路德·贝克

@ user113716我以为里面的函数each不能保证在之前被调用alert。您能解释一下或指向我阅读吗?
Maciej Jankowski

5

我发现很多处理数组而不是json对象的响应。我的解决方案是简单地在增加一个计数器的同时对对象进行一次迭代,然后在遍历该对象以执行代码时,可以增加第二个计数器。然后,您只需将两个计数器进行比较即可获得解决方案。我知道这有点笨拙,但到目前为止,我还没有找到更优雅的解决方案。这是我的示例代码:

var flag1 = flag2 = 0;

$.each( object, function ( i, v ) { flag1++; });

$.each( object, function ( ky, val ) {

     /*
        Your code here
     */
     flag2++;
});

if(flag1 === flag2) {
   your function to call at the end of the iteration
}

就像我说的那样,它不是最优雅的,但是它可以正常工作,而且我还没有找到更好的解决方案。

JP,干杯


1
不,这不起作用。它触发所有的.each,然后触发最终的东西,并且所有内容同时运行。尝试将setTimeout放在您的.each上,您会看到它立即运行flag1 === flag2
Michael Fever

1

如果您愿意采取几个步骤,则可能会起作用。不过,这取决于动画的顺​​序。我不认为这应该是一个问题。

var elems = $(parentSelect).nextAll();
var lastID = elems.length - 1;

elems.each( function(i) {
    $(this).fadeOut(200, function() { 
        $(this).remove(); 
        if (i == lastID) {
           doMyThing();
        }
    });
});

这将起作用-尽管不是我希望的那样。天真地,我正在寻找类似elems.each(foo,bar)的东西,其中foo ='函数应用于每个元素',而bar ='在所有迭代完成后回调”。我将给这个问题更多的时间,看看我们是否遇到了jQuery的某个黑暗角落:)
路德·贝克

AFAIK-您必须在“最后一个”动画完成时触发它,并且由于它们是异步运行的,因此您必须在动画回调中进行操作。我喜欢@Pointy写的更好的版本,因为它不依赖于顺序,而不是我认为那将是一个问题。
tvanfosson

0

关于什么

$(parentSelect).nextAll().fadeOut(200, function() { 
    $(this).remove(); 
}).one(function(){
    myfunction();
}); 

这肯定会调用一次myfunction()(并向我介绍另一个jQuery概念),但是我所寻找的是保证它“何时”运行-不仅仅是它会运行一次。而且,正如帕特里克(Patrick)所提到的,这一部分非常容易。我真正在寻找的是一种在上一次fadeOut逻辑运行之后调用某些东西的方法,我认为.one()不能保证。
路德·贝克

我认为链条可以做到这一点-因为第一部分在最后一部分之前运行。
Mark Schultheiss

0

您必须将其余请求排队,以使其正常工作。

var elems = $(parentSelect).nextAll();
var lastID = elems.length - 1;

elems.each( function(i) {
    $(this).fadeOut(200, function() { 
        $(this).remove(); 
        if (i == lastID) {
            $j(this).queue("fx",function(){ doMyThing;});
        }
    });
});

0

我遇到了相同的问题,并使用以下代码解决了问题:

var drfs = new Array();
var external = $.Deferred();
drfs.push(external.promise());

$('itemSelector').each( function() {
    //initialize the context for each cycle
    var t = this; // optional
    var internal = $.Deferred();

    // after the previous deferred operation has been resolved
    drfs.pop().then( function() {

        // do stuff of the cycle, optionally using t as this
        var result; //boolean set by the stuff

        if ( result ) {
            internal.resolve();
        } else {
            internal.reject();
        }
    }
    drfs.push(internal.promise());
});

external.resolve("done");

$.when(drfs).then( function() {
    // after all each are resolved

});

该解决方案解决了以下问题:使用Deferred对象同步在.each()迭代中启动的异步操作。


您在之前缺少右括号):drfs.push(internal.promise()); 至少我认为以前。
Michael Fever

0

可能是迟到的回复,但是有一个程序包可以处理此 https://github.com/ACFBentveld/Await

 var myObject = { // or your array
        1 : 'My first item',
        2 : 'My second item',
        3 : 'My third item'
    }

    Await.each(myObject, function(key, value){
         //your logic here
    });

    Await.done(function(){
        console.log('The loop is completely done');
    });

0

我正在使用这样的东西:

$.when(
           $.each(yourArray, function (key, value) {
                // Do Something in loop here
            })
          ).then(function () {
               // After loop ends.
          });
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.