实现一个遍历数组的运行器

时间:2012-05-15 00:14:36

标签: javascript jquery asynchronous

BOLD 表示更新。

我有一个数组steps,其内容是具有与之关联的动作和元素的对象。像这样:

steps = [{action: 'click', element: <jQuery element>}, 
         {action: 'click', element: <jQuery element>}, ., ., N]

我想实现一个运行器,它的作用是遍历数组中的每个元素并对元素执行特定操作。每个步骤必须连续执行。例如,如果你有:

 steps = [{action: 'click', element: <jQuery element representing a button>},
          {action: 'click', element: <jQuery element representing an anchor tag>}]

运行run(steps, timeout),将贯穿每一步。步骤[0] .action将在步骤[0] .element上执行。由于步骤[0]可以创建dom元素(通过AJAX )以在步骤[1]中进行交互,因此跑步者需要等待特定时间段(因此,超时) ),调查dom是否存在步骤[1]。元​​素。

这是我迄今为止所做的粗略看法:

var run = function() { 
    $.each(steps, function(i, v) { 
        var interval = 25, 
            start = 0, 
            timeout = 3000; 
        var i = setInterval(function(timeout) { 
            start = start + interval; 
            console.log(start); 
            if ($(v).is(':visible')) { 
                v.click(); 
                console.log('clicked', v); 
                clearInterval(i);
            }   
        }, interval);
    }); 
};

请注意,在上面的示例中,steps只是一个jquery对象数组。它还没有达到理想的格式:

steps = [{action: 'click', element: <jQuery element>}, 
         {action: 'click', element: <jQuery element>}, ., ., N]

这样的'模式'是什么,我需要遵循?我是否需要使用延迟对象来处理这个问题?它是用setTimeout,setInterval实现的吗?谢谢!

最终实施

var run = function(steps, interval, timeout) {
    var timer,
        time = 0,
        i = 0;

    runSingle(steps[0]);

    function abort() {
        console.log("Run aborted");
    }

    function runSingle(step) {
        timer = setInterval(function() {
            time += interval;
            if ($(step.element).is(':visible') === true) {
                clearInterval(timer);
                time = 0;
                $(step.element).trigger(step.action);
                (i < (steps.length - 1)) && runSingle(steps[++i]);
            } else if (time >= timeout) {
                clearInterval(timer);
                abort();
            }
        }, interval);
        console.log("Performed: ", step.action, "on", step.element) 
        if (i === (steps.length - 1)) console.log("Run successful");
    }
}

2 个答案:

答案 0 :(得分:1)

首先请注意,在您的示例中,v变量将代表数组中的对象,因此说v.click()$(v).is(':visible')没有意义 - 您需要想说v.element.click()v.element.is(':visible')

如果你的意思是action将是一个jQuery方法名称的字符串,并且element是一个jQuery对象,那么你可以这样做:

$.each(steps, function(i, obj) { 
    obj.element[obj.action]();
});

如果element是表示应该用于创建jQuery对象的选择器的字符串,那么:

$.each(steps, function(i, obj) { 
    $(obj.element)[obj.action]();
});

除非action可以异步执行某些操作,否则不需要引入轮询概念,例如,如果它执行淡入,或通过Ajax添加元素。

在您的示例中,您似乎申请是否继续当前步骤的唯一标准是当前元素是否可见。如果是这种情况,你可以这样做:

var run = function(steps, delay, timeout) {
             var i = 0,
                 nextStep = function() {
                    if (i < steps.length) {
                       var step = steps[i],
                           retryDelay = 25,
                           retryTotal = 0,
                           intervalId = setInterval(function() {
                              retryTotal += retryDelay;
                              var $el = $(step.element);
                              if ($el.is(':visible')) { 
                                $el[step.action](); 
                                clearInterval(intervalId);
                                i++;
                                setTimeout(nextStep, delay);
                              } else if (retryTotal >= timeout) {
                                clearInterval(intervalId);
                              }
                           }, retryDelay);
                    };
                 }
             nextStep();
};

run(steps, 50, 3000);

run()函数定义nextStep()函数,该函数使用setInterval来检查当前元素是否可见。一旦完成,它将执行操作,清除间隔,并通过setTimeout调用自身来移动到下一个元素。

我不确定如何在轮询中使用超时概念,因为如果当前元素在指定的时间后不可见,您会做什么?你不能真正继续下一个元素,因为它也可能取决于之前的步骤。我想你可以通过清除间隔并且再次调用nextStep()来中止整个事情。 编辑:我已根据最后一句更新了代码。

答案 1 :(得分:1)

这是件事。我没有彻底测试过:

var run = function(steps, interval)
{
    var timer,
        time = 0, timeout = 10000,
        ciel = steps.length - 1,
        i = 0;

    run_single(steps[0]);

    function run_single(item)
    {
        timer = setInterval(function()
        {
            var $el = $(item.selector);

            time += interval;

            if ( $el.length )
            {
                clearInterval( timer );
                time = 0;

                $el.trigger( item.action );

                i < ciel && run_single( step[ ++i ] );
            }
            else
            {
                if ( time >= timeout ) clearInterval( timer );
            }

        }, interval);
    }
};

var steps = [
    {action: 'click', selector: '#first'},
    {action: 'hover', selector: '#second'},
    {action: 'change', selector: '#third'}
    // and so on...
];

run(steps, 100);

在此处查看:[{3}}