最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

JavaScript Promises with AJAX - Stack Overflow

programmeradmin5浏览0评论

I am trying to write a series of AJAX requests into a dictionary. I am attempting to use promises for this, however I am either writing the promise syntax incorrectly, or what I think may be happening is that the function is actually pleting (for loop is done, and AJAX requests sent) but the AJAX requests are still not returned. Therefore this is still returning an empty dictionary.

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);});

Is there a way to "sense" when all of the XMLHTTPRequests have returned?

I am trying to write a series of AJAX requests into a dictionary. I am attempting to use promises for this, however I am either writing the promise syntax incorrectly, or what I think may be happening is that the function is actually pleting (for loop is done, and AJAX requests sent) but the AJAX requests are still not returned. Therefore this is still returning an empty dictionary.

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);});

Is there a way to "sense" when all of the XMLHTTPRequests have returned?

Share Improve this question edited Sep 9, 2019 at 3:15 John 13.7k15 gold badges111 silver badges190 bronze badges asked Nov 1, 2018 at 23:20 MattGMattG 1,9326 gold badges29 silver badges55 bronze badges 4
  • 2 Have you considered using the fetch API? Promises are built in then, and could use Promise.all([]) – pmkro Commented Nov 1, 2018 at 23:25
  • 1 Also, calling a key/value data structure a 'dictionary' is a pythonism. It will seriously confuse JS coders who don't know python. – Jared Smith Commented Nov 1, 2018 at 23:34
  • @MattG Feel free to read my meager Up and Running with Asynchronous JavaScript article – Rafael Commented Nov 2, 2018 at 0:55
  • Useful tutorial here: taniarascia./how-to-promisify-an-ajax-call – Cyril Jacquart Commented Nov 9, 2021 at 20:27
Add a ment  | 

2 Answers 2

Reset to default 7

@Rafael's answer will work, but it doesn't illuminate much about what's going wrong, since you're trying to grok the concept of Promises and write one yourself.

Fundamentally I think your approach has two missteps: 1. creating a single Promise that handles calls to all of your arbitrary list of "activeMachines", and 2. putting your resolve() call in the wrong place.

Usually a Promise looks like this:

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);
});

You can simulate some kind of arbitrary asynchronous work with setTimeout():

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

However your original code puts the resolve() call in a weird place, and doesn't even pass it any data. It looks sorta equivalent to this:

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"
});

Where you're doing a console.log("done"); in your original code is actually where you should be doing a resolve(someData);!

You're also trying to do side effect work inside of your Promise's async function stuff, which is really weird and contrary to how a Promise is supposed to work. The promise is supposed to go off and do its async work, and then resolve with the resulting data -- literally with the .then() chain.

Also, instead of doing multiple asynchronous calls inside of your Promise, you should generalize it so it is reusable and encapsulates only a single network request. That way you can fire off multiple asynchronous Promises, wait for them all to resolve, and then do something.

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 pleted!
  // The value of "data" here will be an array of all your network results
});

The fetch() API is great and you should learn to use that also -- but only once you understand the theory and practice behind how Promises actually operate. :)

Here's an example of the Fetch API which uses Promises by default:

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));

I hope this helps!

发布评论

评论列表(0)

  1. 暂无评论