在进入任何主要框架之前,我目前正在学习Vanilla JS,因此我可以掌握HTTP请求机制。我确定有很多更好的JS库用于HTTP请求,但是我想弄清楚如何使用经典的XMLHttpRequest()解决问题,因此,请不要提出任何不包含XMLHttpRequest()的解决方案。
我试图遍历一个数组,并使用该数组中的信息向API发出HTTP GET请求,然后使用响应数据填充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来一步一步地执行,而是循环首先打开所有请求,然后在它们出现时执行它们,并且在Web调试器中单步执行代码时,代码会四处跳转,这会造成极大的混乱(很抱歉)。
我希望它能逐步执行。我对SO进行了研究,有人建议这些是范围问题,并且应该在单独的函数中提出请求,这就是为什么我在一个函数中使用循环构造代码,而在另一个函数中执行请求的原因,但仍然错误地表现为如前所述。有没有人可以分享他们的专业知识?
答案 0 :(得分:1)
在XMLHttpRequest
完成之后做某事的唯一方法是将“某事”作为回调传递。对于一个请求链,您必须递归执行,因此第一个请求必须接收所有要执行的请求的有序列表,再加上最后一个完成后要执行的finishing回调。代码可能很难看,我不会尝试。
您仍然可以立即发送所有请求,并按照正确的顺序呈现数据。首先创建输出数据的基本框架结构
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;
完成后,将数据放入具有正确ID的卡中。
reqObject[i].onload = function() {
// create the output structure, then
document.getElementById(`card-${i}`).innerHTML = output;
Promise.all(requests).then(//...
。 使用fetch
。
答案 1 :(得分:1)
这是您要尝试的实现示例。
每个请求将被同步一个接一个地执行。
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++]);
答案 2 :(得分:1)
现代JS提供了许多使用异步操作的新方法。我们可以使用promises来代替调用API并尝试每次迭代返回数据的方法。
1)做出一系列承诺以传递给Promise.all
这会使用map
遍历您的数组,以生成一个新的由fetch
生成的promise数组(类似于XMLHTTPRequest但基于promise),并基于该迭代中的array元素生成一个端点。 (我使用了template literal来创建端点字符串,而不是字符串串联)。
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; }
希望有帮助。