Как правильно перебрать HTTP-запросы с помощью XMLHttpRequest ()? - PullRequest
0 голосов
/ 19 апреля 2019

В настоящее время я изучаю vanilla JS, прежде чем переходить к каким-либо основным фреймворкам, чтобы я мог справиться с механикой HTTP-запросов.Я уверен, что существует масса лучших JS-библиотек для HTTP-запросов, но я хочу выяснить, как решить мою проблему с помощью классической XMLHttpRequest (), поэтому, пожалуйста, не предлагайте никаких решений, которые не включают XMLHttpRequest ().

Я пытаюсь перебрать имеющийся у меня массив и сделать HTTP-запрос GET к API с информацией в массиве, а затем заполнить мой HTML данными ответа.Код довольно прост:

Функция, которая принимает мой массив чисел:

function loadUniqueProjs(uniqueArray)
{

    var reqObject = [];

    for (var i in unique_array) 
    {
         outsideLoopRequest(unique_array[i], reqObject, i);
    }


}

Я перебираю массив и выполняю функцию, котораядолжен выполнить мой запрос GET:

function outsideLoopRequest(arrayValue,reqObject, i){

    // Create XHR Object
    reqObject[i] = new XMLHttpRequest();
    reqObject[i].open('GET', 'http://localhost:3000/resource/Projects/' + arrayValue, true)

    reqObject[i].onload = function() {
        if (this.status == 200) {
            var client = JSON.parse(this.responseText);
            var output = '';

            for (var j in client){

                output +=   '<div class="card">'+
                                    '<h5 class="card-header" role="tab" id="heading'+ j + '">'+
                                        '<a data-toggle="collapse" data-parent="#accordion" style="color:black"' + 
                                            'href="#collapse' + j + '"aria-expanded="false" aria-controls="collapse' + j + '"class="d-block collapsed">' +
                                            '<i class="fa fa-angle-double-down pull-right"></i>#' +
                                            client[j].projectTitle + ' | ' + 'PM: ' + client[j].projectManager + ' | ' +
                                            'PO: ' + client[j].projectOwner + ' | ' + 'Estimated Deadline: ' + client[j].predictedCompletion +
                                            ' | ' + 'Status: ' + client[j].status +
                                            ' | ' + 'Requestor: ' + client[j].requestor +
                                        '</a>'+
                                    '</h5>'+
                            '</div>';
            }
    }

    document.getElementById("spinner").hidden = true;
    // output the data into the HTML page
    document.getElementById('accordion').innerHTML = output;

    console.log(this.status + ' Data retrieved successfully!');

}
    reqObject[i].send();
}

После нескольких часов пошаговой отладки я узнал, что HTTP-запросы асинхронны, и при использовании цикла для выполнения запросов один за другим они не будутведите себя так, как вы хотите.Запросы не выполняются один за другим с добавлением необходимого мне HTML-кода, вместо этого цикл сначала открывает все запросы, а затем выполняет их по мере поступления, и при пошаговом выполнении кода в веб-отладчике код перепрыгивает повсюду, где он становится чрезвычайно запутанным(извините за разглагольствования).

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

Ответы [ 3 ]

1 голос
/ 19 апреля 2019

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

var index = 0;
var uniqueArray = ['a', 'b', 'c'];
var output = '';

function http(arrayValue) {
  var url = 'http://localhost:3000/resource/Projects/' + arrayValue;

  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (xhr.readyState == XMLHttpRequest.DONE) {
      onHttpDone(xhr);
    }
  };

  xhr.open('GET', url, true);
  xhr.send();
}

function onHttpDone(xhr) {
  if (xhr.status != 200) {
    console.log('HTTP ERROR: ' + xhr.status);
    return;
  }

  var client = JSON.parse(xhr.responseText);

  output += '<div class="card">' + client.someProperty + '</div>';
  document.getElementById('accordion').innerHTML = output;

  if (index < uniqueArray.length) {
    http(uniqueArray[index++]);
  }
}

http(uniqueArray[index++]);
1 голос
/ 19 апреля 2019

Современный JS предоставляет ряд новых способов работы с асинхронными операциями.Вместо того, чтобы вызывать API и пытаться возвращать данные для каждой итерации, мы можем работать с обещаниями .

1) Сделать массив обещаний для передачи Promise.all

Это повторяет ваш массив с map, чтобы создать новый массив обещаний, созданных с fetch (аналогично XMLHTTPRequest, но на основе обещаний) иконечная точка на основе элемента массива в этой итерации.(Я использовал шаблонный литерал для создания строки конечной точки, а не для конкатенации строк).

const promises = unique_array.map(el => {
  const endpoint = `http://localhost:3000/resource/Projects/${el}`;
  return fetch(endpoint);
});

2) Теперь у нас есть массив обещаний, которые нам просто нужно датьPromise.all который также возвращает обещание с результатом вызова всех этих конечных точек API.Используя эти данные, вы можете затем зациклить их для создания своего HTML.

Promise.all(promises).then(data => {
  // loop over the data to create the HTML
});

Как насчет примера?

// We use a dummy fetch function to mimic a call to an
// API. Data is returned after two seconds. It is an object
// containing a number, and the square of that number
function dummyFetch(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ number: n, square: n * n });
    }, 2000);
  });
}

const arr = [1, 2, 3, 4];

// We produce the array of promises
const promises = arr.map(el => dummyFetch(el));

// When the promises have been resolved/rejected...
Promise.all(promises).then(data => {

  // ...we can iterate over the data and produce an
  // array of html
  const html = data.map(({ number, square }) => {
    return `
      <div>
        <span class="number">${number}</span>
        <span class="square">${square}</span>
      </div>
    `
  }).join('');

  // ...which we can then add to the page 
  document.body.insertAdjacentHTML('beforeend', html);
});
.number { color: red; }
.square { color: blue; }

Надеюсь, это поможет.

1 голос
/ 19 апреля 2019
  1. Единственный способ что-то сделать после завершения XMLHttpRequest - передать это «что-то» в качестве обратного вызова. Для цепочки запросов вам придется делать это рекурсивно, поэтому первая из них должна получить упорядоченный список всех запросов, которые нужно выполнить после, плюс завершающий обратный вызов, который нужно выполнить после завершения последней. Код, вероятно, станет уродливым, я не собираюсь пробовать.

  2. Вы по-прежнему можете отправлять все свои запросы сразу и отображать данные в правильном порядке по мере их поступления. Начнем с создания базовой каркасной структуры выходных данных

var outputStructure = '';

for (var i in unique_array) {
   var cardId = 'card-' + id;
   outputStructure += `<div class="card" id="card-${i}">`;
   outsideLoopRequest(unique_array[i], reqObject, i);
}

document.getElementById('accordion').innerHTML = outputStructure;

и по завершении поместите данные в карточку с правильным идентификатором.

    reqObject[i].onload = function() {
// create the output structure, then
       document.getElementById(`card-${i}`).innerHTML = output;
  1. Проблемы как таковые являются частью того, что в разговорной речи называют «адом обратного вызова». Просто намного проще организовать более сложные асинхронные операции (где некоторые вещи можно выполнять параллельно, другие должны ждать, пока предыдущая не будет выполнена) с обещаниями. Делать вещи после того, как все запросы были выполнены? Одна строка: Promise.all(requests).then(//....

Использование fetch.

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