Уведомление о выполнении отложенного обещания jQuery

Я играл с обещаниями и пытался создать какое-то уведомление о прогрессе.

Код выполняет все функции в правильном порядке, но обновления выполнения выполняются непосредственно перед разрешением, а не тогда, когда они действительно происходят.

Может ли кто-нибудь указать, что я делаю неправильно?

  function start(x) {
    console.log("Start: " + x);
    var promise = process(x);
    console.log("promise returned");
    promise.then(function(data) {
        console.log("Completed: " + data);
    }, function(data) {
        console.log("Cancelled: " + data);
    }, function(data) {
        console.log("In Progress: " + data);
    });
  }

  function process(x) {
    var deferred = $.Deferred();
    var promise = deferred.promise();

    // process asynchronously
    setTimeout(function() {
      for (var i=0 ; i<x ; i++) {
        sleep(1000);
        deferred.notify(i);
      }

      if (x % 2 === 0) {
        deferred.reject(x);
      } else {
        deferred.resolve(x);
      }
    }, 0);

    return promise;
  }

  function sleep(sleepDuration) {
    var now = new Date().getTime();
    while(new Date().getTime() < now + sleepDuration){ /* do nothing */ }
  }

  start(3);

Скрипка здесь: https://jsfiddle.net/n86mr9tL/


person tsw_mik    schedule 29.09.2016    source источник


Ответы (1)


Таймер задержки, реализованный с помощью while(), "блокирует" - т.е. загружает процессор.

Блокировка не только препятствует запуску других javascript, но также препятствует надежному обновлению экрана браузера, включая консоль. Таким образом, хотя эти операторы deferred.notify(i) и console.log("In Progress: " + data) срабатывают, консоль не обновляется до тех пор, пока процессор не освободится для этого.

Неудивительно, что решение заключается в том, чтобы не использовать while().

К счастью, в javascript есть два встроенных метода window.setTimeout() и window.setInterval(), которые концептуально отличаются от while() бездельника, но выполняют ту же роль... без блокировки.

  • window.setInterval(fn, t) запускает функцию fn каждые t миллисекунд,
  • window.setTimeout(fn, t) запускает функцию fn один раз через t миллисекунд.

Оба метода возвращают непрозрачную ссылку, что позволяет их отменить.

В приведенном ниже коде start() не изменено, process() сильно изменено, а sleep() исчезло.

process() теперь делает следующее:

  • создает jQuery Deferred и возвращает производное от него обещание,
  • establises a setInterval() of 1000 milliseconds (1 second), whose function :
    • keeps count of how many times it has been called,
    • вызывает deferred.notify() каждую секунду, пока счетчик i не достигнет заданного максимума x,
  • when the specified maximum is reached :
    • the interval, which would otherwise silently tick away ad infinitum, is cleared,
    • deferred.resolve() или deferred.reject() вызываются для расчета отложенного платежа (и его обещания),
function start(x) {
    console.log("Start: " + x);
    process(x).then(function(data) {
        console.log("Completed: " + data);
    }, function(data) {
        console.log("Cancelled: " + data);
    }, function(data) {
        console.log("In Progress: " + data);
    });
}

function process(x) {
    return $.Deferred(function(dfd) {
        var i = 1;
        var intervalRef = setInterval(function() {
            if(i < x) {
                dfd.notify(i++);
            } else {
                clearInterval(intervalRef);
                dfd[(x % 2 === 0)?'reject':'resolve'](x);
            }
        }, 1000);
    }).promise();
}

console.clear();
start(3);

Обновленная скрипта

person Roamer-1888    schedule 30.09.2016