Необходимо отложить выполнение JavaScript, но setTimeout оказывается проблематичным - PullRequest
1 голос
/ 31 мая 2011

Спасибо, что нашли время помочь мне.

Я пишу игру, в которой анимированный значок поезда движется по заданному пути к пункту назначения, останавливаясь на путевых точках по пути. Это сделано для того, чтобы создать впечатление анимации.

Игра закодирована в Facebook Javascript. Мне нужно найти способ заставить иконку поезда остановиться на 1 секунду, прежде чем перейти к следующей точке маршрута. Я надеялся найти функцию, которая позволила бы мне приостановить выполнение скрипта на одну секунду, но ничего подобного в JS не существует. Поэтому я попробовал setTimeout, но моя основная проблема с этим двоякая:

  1. Мне нужно передать массив в функцию обратного вызова в качестве аргумента, и я не могу понять, как заставить setTimeout сделать это.

  2. Мне наконец удалось использовать setTimeout для выполнения моего кода анимации поезда для 5 путевых точек (я преодолел проблему в 1 с помощью глобальных переменных). К сожалению, похоже, что все пять вызовов setTimeout были поставлены в очередь почти одновременно, что привело к тому, что первый setTimeout сработал одну секунду, а затем все они одновременно сработали, разрушив иллюзию анимации поезда.

Я боролся с этой проблемой в течение шести часов подряд. Было бы замечательно, если бы кто-то мог помочь мне найти решение. Спасибо!

Вот код:

function myEventMoveTrainManual(evt, performErrorCheck) {
      if(mutexMoveTrainManual == 'CONTINUE') {
        var ajax = new Ajax();
        var param = {};
        if(evt) {
          var cityId = evt.target.getParentNode().getId();
          var param = { "city_id": cityId };
        }
        ajax.responseType = Ajax.JSON;
        ajax.ondone = function(data) {
            var actionPrompt = document.getElementById('action-prompt');
            actionPrompt.setInnerXHTML('<span><div id="action-text">'+
              'Train en route to final destination...</div></span>');
            for(var i = 0; i < data.length; i++) {
              statusFinalDest = data[i]['status_final_dest'];
               //pause(1000);
               gData = data[i];
               setTimeout(function(){drawTrackTimeout()},1000);
              if(data[i]['code'] == 'UNLOAD_CARGO' && statusFinalDest == 'ARRIVED') {
                unloadCargo();
              } else if (data[i]['code'] == 'MOVE_TRAIN_AUTO' || data[i]['code'] == 'TURN_END') {
                //moveTrainAuto();
              } else {
                // handle error 
              }
              mutexMoveTrainManual = 'CONTINUE';
            }  
        }
        ajax.post(baseURL + '/turn/move-train-final-dest', param);
      }

}


function drawTrackTimeout() {
  var trains = [];
  trains[0] = gData['train'];
  removeTrain(trains);
  drawTrack(gData['y1'], gData['x1'], gData['y2'], gData['x2'], '#FF0', trains);
  gData = null;
}

Ответы [ 4 ]

2 голосов
/ 31 мая 2011

Как правило, это делается путем создания объекта (скажем, myTrain ), который имеет все свои собственные данные и методы, а затем вызывает метод myTrain.run , который ищет, где поезд есть. Если он находится между двумя станциями, он вызывает себя с setTimeout и говорит, что задержка составляет 50 мс. Когда он достигает станции, он вызывает себя через 1000 мс, создавая паузу в 1 секунду на станции.

Если вы ставите в очередь setTimeouts все сразу, вы рискуете, что все они будут задержаны каким-то другим процессом, тогда все будут запущены одновременно.

Эй, немного веселья (осторожно с упаковкой). Требуется немного практики с хорошим наследованием прототипа:

<!-- All the style stuff should be in a rule -->
<div style="position: relative; border: 1px solid blue;">
  <div id="redTrain"
   style="width:10px;height:10px;background-color:red; position:absolute;top:0px;left:0px;"></div>
</div>

<script type="text/javascript">

// Train constructor
function Train(id) {
  this.element = document.getElementById(id);
  this.timerId;
}

// Methods
// Trivial getPos function
Train.prototype.getPos = function() {
  return this.element.style.left;
}

// Trivial setPos function
Train.prototype.setPos = function(px) {
  this.element.style.left = parseInt(px,10) + 'px';
}

// Move it px pixels to the right
Train.prototype.move = function(px) {
  this.setPos(px + parseInt(this.getPos(),10));
}

// Recursive function using setTimeout for animation
// Probably should accept a parameter for lag, long lag
// should be a multiple of lag
Train.prototype.run = function() {

  // If already running, stop it
  // so can interrupt a pause with a start
  this.stop();

  // Move the train
  this.move(5);

  // Keep a reference to the train for setTimeout
  var train = this;

  // Default between each move is 50ms
  var lag = 50;

  // Pause for 1 second each 100px
  if (!(parseInt(this.getPos(),10) % 100)) {
    lag = 1000;
  }

  train.timerId = window.setTimeout( function(){train.run();}, lag);

}

// Start should do a lot more initialising
Train.prototype.start = function() {
  this.run();
}

// Stops the train until started again
Train.prototype.stop = function() {
  if (this.timerId) {
    clearTimeout(this.timerId);
  }
}

// Set back to zero
Train.prototype.reset = function() {
  this.stop();
  this.setPos(0);
}

// Initialise train here
var myTrain = new Train('redTrain');

</script>
<p>&nbsp;</p>
<button onclick="myTrain.start();">Start the train</button>
<button onclick="myTrain.stop();">Stop the train</button>
<button onclick="myTrain.reset();">Reset the train</button>
0 голосов
/ 31 мая 2011

Вот решение, которое я наконец-то придумал:

function drawTrackTimeout() {
  if(gData != null && gIndex < gData.length) {
    var trains = [];
    trains[0] = gData[gIndex]['train'];
    removeTrain(trains);
    drawTrack(gData[gIndex]['y1'], gData[gIndex]['x1'], gData[gIndex]['y2'], gData[gIndex]['x2'], '#FF0', trains);

    statusFinalDest = gData[gIndex]['status_final_dest'];
    if(statusFinalDest == 'ARRIVED') {
      unloadCargo();
    } else if (gData[gIndex]['code'] == 'MOVE_TRAIN_AUTO' || gData[gIndex]['code'] == 'TURN_END') {
      //moveTrainAuto();
    } else {
      // handle error 
    }
    gIndex++;
  } else {
    clearInterval(gIntid);
    gIntid = null;
    gData = null;
    gIndex = 0;
  }
}


function myEventMoveTrainManual(evt, performErrorCheck) {
//debugger;
      if(mutexMoveTrainManual == 'CONTINUE') {
        var ajax = new Ajax();
        var param = {};
        if(evt) {
          var cityId = evt.target.getParentNode().getId();
          var param = { "city_id": cityId };
        }
        ajax.responseType = Ajax.JSON;
        ajax.ondone = function(data) {
            var actionPrompt = document.getElementById('action-prompt');
            actionPrompt.setInnerXHTML('<span><div id="action-text">'+
              'Train en route to final destination...</div></span>');
            gData = data;
            gIndex = 0;            
            gIntid = setInterval(function(){drawTrackTimeout()},1000);              
        }
        ajax.post(baseURL + '/turn/move-train-final-dest', param);
      }
}
0 голосов
/ 31 мая 2011

Как насчет использования принципов ОО для упрощения проблемы? Создайте «объектный» поезд, который имеет следующие методы:

//train obj
function Train(){
   this.isOnWaypoint = function(){
     return calculateIsWayPoint()
   }

}

//main logic
var train = new Train()
var doneWaiting = false
var doneWaitingTimeout = undefined
var gameLoop = setInterval(1000,function(){
   ...
   if(train.isOnWaypoint() && !doneWaiting){
     if(doneWaitingTimeout == undefined){
       setTimeOut(5000,function(){
         doneWaiting = true
         doneWaitingTimeout = undefined
       })
     }
   }
   ...
})
0 голосов
/ 31 мая 2011

Чтобы передать аргументы, это может помочь вам:

setTimeout(function() {
    (function(arg1, arg2) {
        // you can use arg1 / arg2 here
    })('something', 123);
}, 1000);

Или, если вы используете определенную функцию:

setTimeout(function() {
    someFunction('something', 123);
}, 1000);

Это в основном запускает тайм-аут;через одну секунду функция вызывается с указанными аргументами.

...