Как избежать связанности объектов?

301
29 ноября 2017, 00:11

Есть код, который состоит из трёх классов и функции ready(), которая работает с этими классами. Такой подход представляется мне разумным(хоть и многословным) потому что классы слабо связаны между собой и все промежуточные результаты сохраняются вне их. Таким образом ready() выполняет роль контроллера.

Проблема в том, что один из классов выполняет асинхронную операцию. Поэтому в контроллере ready() я применяю setTimeout(), с значением задержки 1000. Мне кажется, это плохой подход потому что на асинхронный запрос может понадобиться времени меньше 1000ms или больше(зависит от многих фактором, например скорости соединения).

js:

document.addEventListener("DOMContentLoaded", ready);
var file;
var addrArr = [];
function ready() {
    var cSVDownloader = new CSVDownloader();
    cSVDownloader.init();
    setTimeout(function() {
        var cSVConverter = new CSVConverter(file);
        addrArr = cSVConverter.convertRawMapDataToArray();  
        var destroyedBuildings = new DestroyedBuildings('map', addrArr);
        destroyedBuildings.createMap();
    }, 1000);
};
function CSVDownloader() {...}
function CSVConverter() {...}
function DestroyedBuildings() {...}

Полный код здесь.

Один из вариантов, который поможет избавиться от этой неоднозначности - это помещение вызова cSVConverter.convertRawMapDataToArray() в success-функцию XMLHttpRequest-запроса. Но это плохой вариант потому что:

  • в случае нескольких ajax-запросов код превратится в простыню
  • в каждом классе будет вызов другого класса(таким образом нет смысла использовать ООП, достаточно применить процедурный подход)

Представленный код я специально уменьшил до минимального размера(здесь всего 1 ajax-запрос) поэтому пожалуйста не подумайте, что вопрос в том как исправить именно этот код. Вопрос более общий.

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

Answer 1

Советую использовать промисы (Promise) или async/await function.

в 1-ом случае

function ready() {
  var cSVDownloader = new CSVDownloader();
  cSVDownloader.init().then(function() {
      //код начнет выполняться только в случае успешного выполнения асинхронной операции
      var cSVConverter = new CSVConverter(file);
      addrArr = cSVConverter.convertRawMapDataToArray();  
      var destroyedBuildings = new DestroyedBuildings('map', addrArr);
      destroyedBuildings.createMap();
    });
};

ссылка на промисы - https://learn.javascript.ru/promise

во 2-ом случае код примет вид:

async function ready() {
  var cSVDownloader = new CSVDownloader();
  await cSVDownloader.init()
  //тут весь код отрабатывает сихронно
  var cSVConverter = new CSVConverter(file);
  addrArr = cSVConverter.convertRawMapDataToArray();  
  var destroyedBuildings = new DestroyedBuildings('map', addrArr);
  destroyedBuildings.createMap();
});

};

ссылка на async/await - https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Statements/async_function

Answer 2

Проще всего просто передавать функцию для обратного вызова не в setTimeout, а сразу в cSVDownloader.init. Разумеется, нужно изменить эту функцию так, чтобы она вызывала переданный ей callback.

function ready() {
    var cSVDownloader = new CSVDownloader();
    cSVDownloader.init(function() {
        var cSVConverter = new CSVConverter(file);
        addrArr = cSVConverter.convertRawMapDataToArray();  
        var destroyedBuildings = new DestroyedBuildings('map', addrArr);
        destroyedBuildings.createMap();
    });
};

Также можно воспользоваться более современной технологией - обещаниями (Promise). Если cSVDownloader.init будет возвращать обещание - то функцию ready можно будет написать так:

function ready() {
    var cSVDownloader = new CSVDownloader();
    cSVDownloader.init().then(function() {
        var cSVConverter = new CSVConverter(file);
        addrArr = cSVConverter.convertRawMapDataToArray();  
        var destroyedBuildings = new DestroyedBuildings('map', addrArr);
        destroyedBuildings.createMap();
    });
};

А в современных браузерах - даже так:

async function ready() {
    var cSVDownloader = new CSVDownloader();
    await cSVDownloader.init();
    var cSVConverter = new CSVConverter(file);
    addrArr = cSVConverter.convertRawMapDataToArray();  
    var destroyedBuildings = new DestroyedBuildings('map', addrArr);
    destroyedBuildings.createMap();
};
READ ALSO
soket.io несколько emit друг за другом

soket.io несколько emit друг за другом

После загрузке страницы и установки соединения soketio у меня идут несколько emit подряд

229
Как заставить блок с position:absolute скролиться

Как заставить блок с position:absolute скролиться

Есть вот такой пример (код ниже) там при скролле шапка опускается вниз и если нажать на серые кнопки, то к элементам меню создаются цветные...

261