Страницы

понедельник, 3 марта 2014 г.

JavaScript. Небольшой этюд на тему неблокирующего кода

Задача: создать объект, который через определенные промежутки времени будет обновлять свои свойства или, к примеру, сообщать другим объектам о своем самочувствии. Вопрос: что может случиться, если "неправильно" этот самый объект удалить. По мотивам одного фэйла...

Сразу хочу предложить пару примеров на Node.js.

test1.js:
global.obj = new Obj(Date.now());
log('created');

setTimeout(function() {
  delete global.obj;
  log('deleted');  
}, 15000);

function Obj(dt) {
  this.dt = dt;  
  var self = this;  

  setInterval(function() {
    self.dt = Date.now();  
    log();
  }, 5000);
}

function log(s) {
  console.log('global.obj %s: %s', s || '', JSON.stringify(global.obj));  
}

test2.js:
global.obj = new Obj(Date.now());
log('created');

setTimeout(function() {
  delete global.obj;
  log('deleted');  
}, 15000);

function Obj(dt) {
  this.dt = dt;  
  var self = this;
  
  (function schedule() {
    setTimeout(function() {
      self.dt = Date.now();
      log();
      schedule();
    }, 5000);
  }());  
}

function log(s) {
  console.log('global.obj %s: %s', s || '', JSON.stringify(global.obj));  
}

Вопрос: сколько времени будет выполняться этот код?
Пока не выполнишь - не догонишь :) :

Догадались, в чем подвох?

Решение: перед удалением объекта не забыть завершить процессы, выполняющиеся асинхронно, для чего необходимо предусмотреть какой-нибудь механизм.

test3.js:
global.obj = new Obj(Date.now());
log('created');

setTimeout(function() {
  clearInterval(global.obj.interval);
  delete global.obj;
  log('deleted');  
}, 15000);

function Obj(dt) {
  this.dt = dt;  
  var self = this;  

  this.interval = setInterval(function() {
    self.dt = Date.now();  
    log();
  }, 5000);
}

function log(s) {
  console.log('global.obj %s:', s || '');
  console.dir(global.obj);
}

test4.js:
global.obj = new Obj(Date.now());
log('created');

setTimeout(function() {
  global.obj.interval = false;
  delete global.obj;
  log('deleted');  
}, 15000);

function Obj(dt) {
  this.dt = dt;  
  var self = this;

  this.interval = true;

  (function schedule() {    
    setTimeout(function() {
      self.dt = Date.now();
      log();
      if (!self.interval) return;
      schedule();
    }, 5000);
  }());  
}

function log(s) {
  console.log('global.obj %s:', s || '');
  console.dir(global.obj);
}

После выполнения кода test3.js вывод в консоль получился довольно большой, потому как там имеют место циклические ссылки, поэтому целиком его не даю, а после выполнения test4.js все должно завершиться примерно так:

В общем памятуем об особенностях потока выполнения JavaScript.