等待回调后再继续循环

37

我有一个for循环,正在进行遍历。

我想制作一个自定义模态框,并在继续之前等待响应。

我该如何实现?我知道我必须等待回调。

像这个例子:

 for(var x in array){
            alert(x);
            console.log(x);
        }

它完全做到了我想要的。但是,我想要有三个按钮。 但alert不是javascript的一部分吗?(它在浏览器中。)

那么,你们有什么想法吗?

我在考虑像这样做:

var run = true;
function foo(){
    if (run){
        setTimeout(foo, 500);
    }
}

function stop(){
    run = false;
}

foo();

然后等待停止,直到点击按钮才继续。但这真的是一种好的实践吗?

或者将lambda函数用作customAlert的参数,并使用一个“全局”变量来保存我正在遍历的数组的当前位置,并使用函数进行操作。例如:检查数组是否仍然包含大于X的键。然后再次执行函数,并每次增加全局X。

感谢lostsource提供的代码: 哦,我有一个想法;我将简单地在匿名函数中使用lostsource的解决方案,这样我就不会得到全局变量。太棒了。

(function(){

})();

你是说因为alert不支持足够的选项,所以你实际上不能使用它? - Charlie Wallace
不能简单地用自定义模态框替换alert(),并让它像alert一样运行。 - Kevin B
顺便提一下,alert是同步的。你不能像那样停止函数执行 - 除非使用JS1.7的协程/ yield,但这不太跨浏览器,并且需要在脚本类型上定义一个version。你应该将i存储在执行上下文中,并将其传递给一个函数,该函数使用它进行一次迭代,调用您的样式化警报,再次将递增的i传回您的迭代器函数。 - Fabrício Matté
@FabrícioMatté 我编辑了我的帖子。你认为这是一个好的实践吗? :/ - Muqito
是的,Lostsource的代码模板比我想象的还要整洁。 - Fabrício Matté
2个回答

89

假设这是你的数组

var list = ['one','two','three'];
您可以尝试使用这种循环/回调方法。
var x = 0;
var loopArray = function(arr) {
    customAlert(arr[x],function(){
        // set x to next item
        x++;

        // any more items in array? continue loop
        if(x < arr.length) {
            loopArray(arr);   
        }
    }); 
}

function customAlert(msg,callback) {
    // code to show your custom alert
    // in this case its just a console log
    console.log(msg);

    // do callback when ready
    callback();
}

使用方法:

// start 'loop'
loopArray(list);

JSFiddle在这里:http://jsfiddle.net/D9AXp/


是的,我也有这样的想法(关于使用lambda函数):)非常感谢。 - Muqito
3
非常有帮助,谢谢。太棒了,我刚刚使用了arr.length-1。 - Bakly
实际上,如果回调函数需要执行任何异步操作,我认为这种方法不起作用。请检查一下,如果有解决方案,我很想知道,因为我正在尝试逐个加载需要一些时间的图像文件:https://jsfiddle.net/evejweinberg/tydfbop6/1/ - EJW
你只需要在成功返回或错误返回中放置回调函数。 - Andrew Paes
这个想法是:“编写一个循环,直到每个请求调用/完成其回调函数(类似于常规的for循环)才不会前进到下一个索引/项”。上面的示例非常简单但有效。谢谢@lostsource。 - Panini Luncher
尝试了相同的事情,但不起作用,您能提供一些指导吗?https://stackoverflow.com/questions/51347216/then-function-getting-executed-before-all-the-asynchronous-calls-are-executed - Raphael

4

这个解决方案依赖于jQuery(1.7+)和jQuery UI的dialog,但是它被实现为Array原型的自定义方法,而不是jQuery插件。

下面是Array方法的代码:

Array.prototype.runDialogSequence = function(dialogCallback, startIndex, endIndex){
    startIndex = Math.max(0, startIndex || 0);
    endIndex = Math.min(this.length - 1, endIndex || this.length - 1);
    var sequenceIndex = 0,
        arr = this,
        dfrd = $.Deferred().resolve(startIndex);
    function makeCloseFn(seqData){
        return function(event, ui){
            if(seqData.continue_) { seqData.dfrd.resolve(seqData.arrayIndex+1, seqData); } //continue dialog sequence
            else { seqData.dfrd.reject(seqData.arrayIndex, seqData); } //break dialog sequence
        }
    }
    $.each(this, function(i){
        if(i < startIndex || i > endIndex) { return true; }//continue
        dfrd = dfrd.then(function(arrayIndex){
            var seqData = {
                dfrd: $.Deferred(),
                arrayIndex: arrayIndex,
                sequenceIndex: ++sequenceIndex,
                length: 1 + endIndex - startIndex,
                item: arr[arrayIndex],
                continue_: false
            };
            dialogCallback(seqData).on("dialogclose", makeCloseFn(seqData));
            return seqData.dfrd.promise();
        });
    });
    return dfrd.promise();
}

Array.runDialogSequence() 依赖于以下内容:

  • 在文档正文中适合用于填充文本/值的对话框模板。
  • 类似项目的数组(通常是JavaScript对象),其中包含按顺序填充对话框所需的数据。
  • 将正确构造的 "openDialog" 函数作为第一个参数传递。

以下是带有解释性注释的示例 "openDialog" 函数:

function openDialog(seqData){
    /*
    seqData is an object with the following properties:
        dfrd: A Deferred object associated with the current dialog. Normally resolved by Array.runDialogSequence() to run through the sequence or rejected to break it, but can be resolved/rejected here to force the dialog sequence to continue/break. If resolved, then pass (seqData.arrayIndex+1, seqData), or custom values. If rejected, typically pass (seqData.arrayIndex, seqData).
        arrayIndex: The current array index.
        sequenceIndex: The current index within the sequence. Normally the same as arrayIndex but Differs when a non-zero startIndex is specified.
        length: The full length of the dialog sequence.
        item: The current item from the array.
        continue_: (false) Set to true to allow the sequence to continue.
    */
    var item = seqData.item;
    var $d = $("#d");
    $d.dialog({
        title: 'Dialog (' + seqData.sequenceIndex + ' of ' + seqData.length + ')',
        modal: true,
        buttons: {
            "Continue": function(){
                seqData.continue_ = true;//set to true before closing to go to next dialog.
                $(this).dialog("close");
            },
            "Cancel": function(){
                $(this).dialog('close');//closing with seqData.continue_ set to its default value false will break the dialog sequence.
            }
        }
    }).find("#message").text(item);//Typically you will do a lot more here to populate the dialog with item data.
    return $d;//openDialog() must return a dialog container, jQuery-wrapped.
}

Array.runDialogSequence() 返回一个 jQuery promise,允许在对话序列完成(done 函数)或在序列中断时执行自定义操作(fail 函数)。

以下是几个示例调用:

//Simplest possible
$("#myButton1").click(function(){
    myArray.runDialogSequence(openDialog);
});

//Call with custom startIndex and endIndex, and chanined `.then()` to provide custom actions on break/completion.
$("#myButton2").click(function(){
    myArray.runDialogSequence(openDialog, 1, 3).then(function(i, seqData){
        alert('All dialogs complete - last item = "' + seqData.item + '"');
    }, function(i, seqData){
        alert('Dialog sequence was broken at item ' + i + ' "' + seqData.item + '"');
    });
});

演示

这是我所能想到的最通用的解决方案。


谢谢MaggiQall,有趣的是看看将来是否有人会发现这个有用。 - Beetroot-Beetroot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接