1

Есть такой код:

var artAjaxRequest = new function ()
{
  var xhr;

  xhr = new XMLHttpRequest();
  xhr.open('GET', '/assets/json/arts.json', true);
  xhr.send();

  xhr.onload = function(){
    // Тут мы парсим json и формируем массив картинок
  }

  this.getNewArts = function(){
    // Эта функция занимается тем, что выводит картинки на страницу
  };
}();

Исполняется это всё "хозяйство" через artAjaxRequest.getNewArts();.
Но есть тут проблема: когда я пишу где-то в коде artAjaxRequest.getNewArts();, выполняется код getNewArts, тогда как сам json ещё не "прилетел" и функция onload не отработала своё. Почему? Потому что ajax-запрос не выполняется так быстро, как с кодом разбирается браузер.
Соответственно, json не распарсен и в функции getNewArts возникают ошибки.
И вот вопрос: как сделать так, чтобы .getNewArts выполнялась после того, как отработает xhr.onload?
Но не спешите помещать artAjaxRequest.getNewArts(); внутрь onload. Загвозка в том, что метод getNewArts() будет вызываться часто, а не один единственный раз, когда прилетает json.
Буду благодарен за разъяснения и советоы по улучшению композиции кода.

wokalek
  • 1,284
  • @vp_arth, что значит откуда? Не понял. При чем тут браузер ФФ? – wokalek Mar 01 '17 at 21:14
  • @vp_arth, ну событий-то много, тут можешь глянуть сам. – wokalek Mar 01 '17 at 21:18
  • не спешите помещать artAjaxRequest.getNewArts(); внутрь onload Непонятно, почему нет. – vp_arth Mar 01 '17 at 21:19
  • @vp_arth, ниже IE9 не поддерживаются. Но кому будет не наплевать на IE9 в 2017-м году - вот в чем вопрос... – wokalek Mar 01 '17 at 21:19
  • @vp_arth, я же сказал почему. Прочитай ещё раз, что я написал. – wokalek Mar 01 '17 at 21:20
  • Прочитал, не понял, что мешает вызывать этот метод столько раз сколько нужно. – vp_arth Mar 01 '17 at 21:20
  • @vp_arth вызвать какой метод? onload? Это обработчик. Он срабатывает 1 раз, когда запрос завершается (успешно или нет). Это будет нелогично - парсить json 20 раз. Насчет другого я пояснил в вопросе. – wokalek Mar 01 '17 at 21:22
  • "метод getNewArts() будет вызываться часто, а не один единственный раз" – vp_arth Mar 01 '17 at 21:22
  • @vp_arth, поэтому нельзя засунуть getNewArts() в обработчик onload, потому что представь, что artAjaxRequest.getNewArts(); много раз присутствует в коде при разных условиях. JSON не успеет придти, а код getNewArts() будет выполняться (вернее - не будет, ведь будут ошибки, так как json, которого ещё нет, нужно ещё распарсить). – wokalek Mar 01 '17 at 21:24
  • Ну и вот до кучи, ассоциация http://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call – vp_arth Mar 01 '17 at 21:33
  • @vp_arth, я сейчас накопал про promis'ы... Возможно, это то, что я ищу. Спасибо. – wokalek Mar 01 '17 at 21:40

1 Answers1

2

Если правильно понял, то можно завести переменную для сформированного результата в области видимости функции.
Также можно реализовать паттерн Наблюдатель для своевременного оповещения о полученных данных.

var artAjaxRequest = new function ()
{
  var xhr;

  xhr = new XMLHttpRequest();
  xhr.open('GET', '/assets/json/arts.json', true);
  xhr.send();

  var response;

  var subscribers = [];

  xhr.onload = function(){
    // Тут мы парсим json и формируем массив картинок
    response = parsedJSON;

    // Оповещаем подписчиков
    subscribers.forEach(function(sub) {
      sub();
    });
    subscribers.length = 0;
  }

  this.getNewArts = function(next){
    if (!response) {
      // Подписываемся
      subscribers.push(getNewArts.bind(this, next));
      return;
    }
    // Эта функция занимается тем, что выводит картинки на страницу
    next(something)
  };
}();
vp_arth
  • 27,179
  • Прекрасное решение(нет :D). Это первое, что я сделал. Кстати, сразу скажу, что эта переменная уже есть. Но погоды это не делает. Потому что скрипт как выполняется при асинхронном запросе? Выполняется тупо напрямик после xhr.send(). Если в коде где-то встречается artAjaxRequest.getNewArts();(имею ввиду за пределом этой функции, просто используется где-то), то что вернет функция getNewArts()? Ничего. А надо, чтобы отрабатывала свое. Как выполнять getNewArts() после того, как придет ответ от onload? Это и есть мой вопрос. – wokalek Mar 01 '17 at 21:29
  • Ну тогда у вас дубликат, батенька) Сейчас найду) – vp_arth Mar 01 '17 at 21:31
  • Я думаю ты найдешь совет Утки про onload. Если так, то я буду с пеной у рта добиваться отмены дубликата и потом конкурс сделаю, а администрации пожалуюсь на это. Вот так вот. – wokalek Mar 01 '17 at 21:32
  • Ничего плохого в вопросе-дубликате нет. Просто решение этой проблемы уже есть в другом вопросе. Дословно ваш вопрос звучит как "Как вернуть результат асинхронной функции?" – vp_arth Mar 01 '17 at 21:40
  • Нет. Как вернуть результат асинхронной функции - легко. Вопрос в другом. Как выполнять функцию после асинхронной функции, но чтобы сам вызов этой функции не игнорировался при неготовности асинхронной функции, а выполнялся как она будет уже готова. Короче мне не нужно писать в getNewArts() return;, потому что мне нужно, чтобы она отработала своё, а не выполнилась как заглушка. Нужно что-то eventListner, но не на объект DOM, а на переменную xhr.status (которая говорит о завершении запроса). – wokalek Mar 01 '17 at 21:44
  • Да ну это тупой код, ты же сам понимаешь это. Даже постыдился бы такое предлагать, ну. Не буду я таймаутами пользоваться. – wokalek Mar 01 '17 at 21:48
  • А с чего ты взял, что мне нужно ждать каждые 50 милисекунд? Может 25? Или 14? Нет, это нерациональный код и для собственного проекта такое делать не стану. Тут можно и попотеть. – wokalek Mar 01 '17 at 21:52
  • Окей, добавил в ответ Observer – vp_arth Mar 01 '17 at 21:56
  • Слушай, а второе решение вполне себе. Я думаю даже, что оно сработает как надо. Правда я всё равно промисы дочитаю завтра, может они лучше подойдут. И пример оригинальный с подписчиками) Единственное, что не понятно, так это как ты это называл - Observer. Я не так давно нагонялся с MutationObserver, который через пень-колоду работает в FF, его напоминает, я думал ты через него сделал (что невозможно, ведь он следит за ДОМ). А по сути это обычный массив с bind, который будет прогонен после загрузки json (что, в принципе, за решение сойдет). Но я попозже его отмечу как решение. Пока не уверен. – wokalek Mar 01 '17 at 22:05
  • Шаблоны проектирования такие шаблоны... Сделал ссылку на почитать. – vp_arth Mar 01 '17 at 22:07