Как передать параметр в обработчик

Привет всем! Уже измучился над вопросом, как передать нужный параметр в обработчик события…
Есть кусочек кода:

let cities;

function getData(method, url, cb) {
  const xhr = new XMLHttpRequest();
  xhr.open(method, url);
  xhr.send();
  xhr.addEventListener("load", function() {
    switch (xhr.status) {
      case SUCCESS_CODE:
        cities = JSON.parse(xhr.response);
        if (cb) {
          cb();
        }

        break;

      default:
        console.error();
    }
  });
}

getData(METHOD, PROXY_URL + API_URL, removePopup);

В таком виде она работает прекрасно - с сервера приходит массив, по замыканию берётся переменная cities и в неё этот массив записывается, при этом я использую его дальше и что-то с ним делаю. Проблема в том, что мне эта функция нужна для запроса данных по другим URL, и другие массивы я хочу записывать в другие переменные. Как сделать так, чтобы я мог подставлять не только cities?
На данный момент придумал только 1 вариант, но он не рабочий:


function getData(method, url, arr, cb) {
  const xhr = new XMLHttpRequest();
  xhr.open(method, url);
  xhr.send();
  xhr.addEventListener("load", function() {
    switch (xhr.status) {
      case SUCCESS_CODE:
        arr = JSON.parse(xhr.response);
        if (cb) {
          cb();
        }

        break;

      default:
        console.error();
    }
  });
}
getData(METHOD, PROXY_URL + API_URL, cities, removePopup);

Подскажите, пожалуйста, как это сделать.
UPD: попробовал сделать так:

function parseResponse(xhr, cit, cb) {
  return function() {
    switch (xhr.status) {
      case SUCCESS_CODE:
        cit = JSON.parse(xhr.response);
        if (cb) {
          cb();
        }

        break;

      default:
        console.error();
    }
  };
}

function getData(method, url, arr, cb) {
  const xhr = new XMLHttpRequest();
  xhr.open(method, url);
  xhr.send();
  xhr.addEventListener("load", parseResponse(xhr, arr, cb));
}

getData(METHOD, PROXY_URL + API_URL, cities, removePopup);

Всё равно - не работает.

Ты же там передаешь коллбек, который потом вызываешь? Можно в коллбек передать результат параметром.
Ну или обернуть все содержимое getData в Promise, и передавать cities или другой результат в resolve этого промиса.

Можете попроще объяснить первый вариант, как именно передать результат параметром? :) Мне казалось, я так и делал. Только мне не результат нужно передать, а ту переменную, в которую этот результат записать

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

const getData = (params, callback) => {
  // request logic here
  const cities = JSON.parse(xhr.response);

  callback(cities); // это будет removePopup
}

const removePopup = (requestData) => {
  // handle data, которую ты передал в getData. В этом случае это твой cities
}

getData({ method: 'get', url: '/cities' }, removePopup);

А не работает потому, что твоя переменная оказывается замкнутой внутри функции getData. Переопределять-то он ее переопределяет, но наружу этого не видно, потому что переменная перезаписывается в локальном scope.

Прости, я, наверное, плохо объяснил: проблема не в removePopup - она ничего не должна получать, это просто экранчик, который не даёт взаимодействовать со страничкой, пока не загрузились полностью данные. :) Для простоты я его уберу. И второй вариант, как ни странно, оказался рабочий. Проблема чуть в другом месте: эти данные, которые запишутся в нужную мне переменную, нужны для инпута, при клике на который должен быть показан список из пришедших с сервера данных. Я сделал пуш, посмотри, пожалуйста, если не трудно: https://alessio18911.github.io/

В репе у тебя проблема номер один в том, что ты в самом начале определяешь

let cities = "Города";

а потом везде передаешь ее параметром везде

getData(METHOD, PROXY_URL + API_URL, cities); // здесь cities это строка

а в функции ты ожидаешь, что это будет массив:

function getData(method, url, arr, cb) { // а здесь уже массив

при этом передаешь ты три аргумента, а ожидаешь четыре.

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

Хорошо. Т.е. нужно сделать так:

function parseResponse(xhr) {
  return function() {
    switch (xhr.status) {
      case SUCCESS_CODE:
        return JSON.parse(xhr.response);

      default:
        console.error();
    }
  };
}

function getData(method, url) {
  let arr;
  const xhr = new XMLHttpRequest();
  xhr.open(method, url);
  xhr.send();
  xhr.addEventListener("load", parseResponse(xhr));
  return arr;
}
const cities = getData(METHOD, PROXY_URL + API_URL);
? 
Но всё равно, учитывая 
departureInput.addEventListener("input", function(evt) {
  showCities(evt, departureDropdown, cities);
});

destinationInput.addEventListener("input", function(evt) {
  showCities(evt, destinationDropdown, cities);
});

cities не успеет вместить в себя массив и всё равно я имею ошибку, потому что он будет фильтровать пустую переменную ((( Уф, эти события - это ужас. Я никак не могу понять, КАК из обработчика ВЫРВАТЬ данные, когда они уже ЗАГРУЗЯТСЯ, чтобы потом передать их в другой обработчик (((( В любом случае, спасибо, что находишь время посмотреть и подсказать :)

Если ты все-таки хочешь использовать глобальную переменную, то можно сделать вот так:

let cities = [];
////

function parseResponse(xhr, cb) {
  return function() {
    //
    cb(JSON.parse(xhr.response));
  };
}

function getData(method, url, cb) {
  //
  xhr.addEventListener("load", parseResponse(xhr, cb));
}

getData(METHOD, PROXY_URL + API_URL, (data) => {
  cities = data;
});

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

1 лайк

Спасибо тебе огромное! Всё заработало! :)

Стратегически такой подход ошибочен. @kprudnikov прав что нужно данные передавать аргументами в коллбеке. Оставляй пока так, но морально будь готов поменять подход когда структура кода перестанет справляться с новыми требованиями.