JavaScript Promises与AJAX

9

我正在尝试将一系列AJAX请求写入字典中。 我尝试使用promise来实现,但是我可能要么错误地编写了promise语法,要么我认为可能发生的情况是函数已经完成(for循环已完成,并发送了AJAX请求),但是AJAX请求仍未返回。 因此,这仍然返回一个空字典。

let dict = {};
let activeMachines = ["41", "42", "43"];
let dataPromise = new Promise (function (resolve, reject)
{
 for (let i = 0; i < activeMachines.length; i++)
 {
  let machineID = activeMachines[i]
  let getAPIData = new XMLHttpRequest();
  let url = 'http://127.0.0.1:8000/processes/apidata/' +machineID + '/';
  getAPIData.open('GET', url);
  getAPIData.send();
  getAPIData.onload = function()
  {
   let APIData = JSON.parse(getAPIData.responseText);
   dict['machine_' + machineID] = APIData[0].author_id;
   dict['temp' + machineID] = APIData[0].tempData; //get value
   dict['humid' + machineID] = APIData[0].humidData;
   timeValue = String((APIData[0].dateTime));
   dict['time' + machineID] = new Date(timeValue);
   console.log("done");
  }
 }
 resolve();
});

dataPromise.then(function() {console.log(dict);});

有没有一种方法可以“感知”所有XMLHTTPRequests何时返回?

2
你考虑过使用fetch API吗?Promises内置于其中,可以使用Promise.all([]) - pmkro
1
此外,将键/值数据结构称为“字典”是Python的专用术语。这会严重困惑不了解Python的JS程序员。 - Jared Smith
@MattG 欢迎阅读我的Asynchronous JavaScript入门指南文章,随意发表您的看法。 - Rafael
这里有一个有用的教程:https://www.taniarascia.com/how-to-promisify-an-ajax-call/ - Cyril Jacquart
2个回答

7

@Rafael的答案可以解决问题,但它并没有很好地阐明出问题所在,因为你试图理解Promise的概念并自己编写一个。

基本上,我认为你的方法有两个错误:1.创建一个单一的Promise来处理对所有任意列表"activeMachines"的调用,2.将你的resolve()调用放错了位置。

通常一个Promise看起来像这样:

const myPromise = new Promise(function(resolve, reject) {
  doSomeAsyncWork(function(result) {
    // Some kind of async call with a callback function or somesuch...
    resolve(result);
  });
}).then(data => {
  // Do something with the final result
  console.log(data);
});

您可以使用setTimeout()模拟任意异步工作:

const myPromise = new Promise(function(resolve, reject) {
  // Resolve with "Done!" after 5 seconds
  setTimeout(() => {
    resolve("Done!");
  }, 5000);
}).then(data => {
  console.log(data); // "Done!"
});

然而,您原来的代码在一个奇怪的地方调用了 resolve() 方法,甚至没有传递任何数据。它看起来有点等同于这样:
const myPromise = new Promise(function(resolve, reject) {
  // Resolve with "Done!" after 5 seconds
  setTimeout(() => {
    // Doing some work here instead of resolving...
  }, 5000);
  resolve();
}).then(data => {
  console.log(data); // This would be "undefined"
});

在原始代码中使用console.log("done");的位置实际上应该是使用resolve(someData);的位置!您还尝试在Promise的异步函数中执行副作用操作,这非常奇怪,与Promise的预期工作方式相反。Promise应该完成其异步工作,然后使用结果数据解决--字面上使用.then()链。
此外,您应该将多个异步调用封装在一个Promise中,使其可重用并封装仅单个网络请求。这样,您可以启动多个异步Promises,等待它们全部解决,然后再执行某些操作。
const activeMachines = ["41", "42", "43"];

// Make a reusable function that returns a single Promise
function fetchAPI(num) {
  return new Promise(function(resolve, reject) {
    const getAPIData = new XMLHttpRequest();
    const url = "http://127.0.0.1:8000/processes/apidata/" + num + "/";
    getAPIData.open("GET", url);
    getAPIData.send();
    getAPIData.onload = function() {
      const APIData = JSON.parse(getAPIData.responseText);
      const resolveData = {};
      resolveData["machine_" + num] = APIData[0].author_id;
      resolveData["temp" + num] = APIData[0].tempData; //get value
      resolveData["humid" + num] = APIData[0].humidData;
      timeValue = String(APIData[0].dateTime);
      resolveData["time" + num] = new Date(timeValue);
      resolve(resolveData);
    };
  });
}

// Promise.all() will resolve once all Promises in its array have also resolved
Promise.all(
  activeMachines.map(ea => {
    return fetchAPI(ea);
  })
).then(data => {
  // All of your network Promises have completed!
  // The value of "data" here will be an array of all your network results
});
< p > fetch() API 很棒,你应该学习如何使用它 - 但只有在你理解 Promise 的原理和实践后才能使用。 :)

1
谢谢@jered,这个方法很有效!我显然不太理解Promise,所以这对我帮助很大。 - MattG

4

这里是使用Promise默认的Fetch API的示例:

let m_ids = [1,2,3,4];
let forks = m_ids.map(m => fetch(`http://127.0.0.1:8000/processes/apidata/${m}`));
let joined = Promise.all(forks);

joined
    .then(files => console.log('all done', files))
    .catch(error => console.error(error));

希望这能帮到你!

谢谢 Rafael!如何使用Fetch API访问JSON值? - MattG
再次感谢@Rafael。这比我的代码简单得多,但是我似乎无法获取JSON。我想我只需要更多地了解Fetch API。我会阅读你的文章。再次感谢! - MattG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接