pause loop for a function to return a result from esrirequest

482
9
Jump to solution
02-28-2024 09:35 AM
LefterisKoumis
Occasional Contributor III

I have this issue that I tried with promises and async/await to pause each iteration of the loop until a function completes its task with an esrirequest. 

Here is an example. Before I get the response (#23) the loop proceed to the next iteration.

 

function loop() {
  var variable =null
  for (let j = 0; j < records.length; j++) {
    let thestate = records[j];
    get_results(thestate);
    if (variable){
     //extra work
  }
}

async function get_results(state) {
  let getinfo =
    "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/2/query?f=json&Where=STATE_NAME='";
  params = state;
  params += "'&outFields=NAME,HISPANIC";

  getinfo = getinfo + encodeURI(params);

  var options = {
    query: {
      f: "json",
    },
    responseType: "json",
  };
  await esriRequest(getinfo, options).then((response) => {
    console.log(response);
    variable = response.data.var
  
    
  });
}

 

  

1 Solution

Accepted Solutions
ReneRubalcava
Frequent Contributor

If you make your loop async, I think you can get the behavior you're looking for.

https://codepen.io/odoe/pen/MWRWwax?editors=0010

If I was doing this from scratch, I might set it up a bit different, so that the variable was a returned value and not set on each iteration, but this seems to work.

View solution in original post

9 Replies
KenBuja
MVP Esteemed Contributor

There is a for await...of loop that you can use in this case.

LefterisKoumis
Occasional Contributor III

THank you Ken. I don't think in my case it will work. I didn't explain my issue well. I changed my script. 

THe esriRequest will produce a value (#27) which it will be used on #6 before the next iteration.

0 Kudos
JeffreyWilkerson
Occasional Contributor III

I'm not sure what you need to set an await for, just handling the Esri Request response should be sufficient, as in:

function get_results(state) {
  let getinfo =
  "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/2/query?f=json&Where=STATE_NAME='";
  params = state;
  params += "'&outFields=NAME,HISPANIC";

  getinfo = getinfo + encodeURI(params);
  esriRequest(getinfo, {
	responseType: "json"options
	}).then(function (response) {
		// Do something like
		let thisVal = response.data.fields[0].domain.codedValues;
		optionsSelector.push({value: thisVal, label thisVal.name});
		console.log(response);
	}).catch(function (error) {
		console.error("esriRequest Error: " + error);
	});
  });
}

But if you need to wait for something you can always push that into a promise, like:

function get_results(state) {
	let defStateQuery = new Deferred();
	let promiseQueries = []
	promiseQueries.push(defStateQuery)
	setTimeout(function (inState) {
		let getinfo = "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/2/query?f=json&Where=STATE_NAME='";
  	params = inState;
  			params += "'&outFields=NAME,HISPANIC";

  		getinfo = getinfo + encodeURI(params);

  		//var options = { query: {f: "json",}, responseType: "json"};

  		esriRequest(getinfo, {
			responseType: "json"options
			}).then(function (response) {
				// Do something like
				let thisVal = response.data.fields[0].domain.codedValues;
				optionsSelector.push({value: thisVal, label thisVal.name});
				console.log(response);
				deferred.resolve(response);
                        	return deferred.promise
			}).catch(function (error) {
				console.error("esriRequest Error: " + error);
				deferred.reject(error);
                    		return deferred.promise;
			});
  		});
	}, 0);
	return deferred.promise;
}

all(promiseQueries).then(function (results) {
	console.log(results);
});

This sample allows you to enter a URL and see the results, but the REST service seems to not be working (maybe there's too many of us trying to write a response, LOL). 

https://developers.arcgis.com/javascript/latest/sample-code/request/ 

0 Kudos
LefterisKoumis
Occasional Contributor III

Thank you Jeffrey. 

I modified my script to explain better. 

The esriRequest will produce a value (#27) which it will be used on #6 before the next iteration.

The script presently will not pause the loop awaiting the processing of #6 in my script.

Also in your script, I assume that there is no option on   #15 (second script) since you define options in esriRequest.

 

0 Kudos
JeffreyWilkerson
Occasional Contributor III

I went back and looked what I had done for 4.24, and it relied on Dojo promises. Esri just talks about the .then/.catch method of working with promises, but in a script I had developed I as passing the promise (deferred) among multiple functions and only after successfully completing the last one did I resolve (or reject) the promise. Then, the program waited for all function calls to finish for all records, which as accomplished using the Dojo 'All' function. Looks like this is not available anymore as 4.29 now seems devoid of all Dojo, and after looking at possibly adding it to the libraries, I thought the better of it and decided I should figure out how to do this with native Javascript promises. 

This may not be the correct way, but it works. You just have to parse through all of the resulting features for each call to get (in this case) the value for 'Hispanic':

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
    <title>Request data from a remote server | Sample | ArcGIS Maps SDK for JavaScript 4.29</title>
    <link rel="stylesheet" href="https://js.arcgis.com/4.29/esri/themes/light/main.css" />
    <script src="https://js.arcgis.com/4.29/"></script>
    <script type="module" src="https://js.arcgis.com/calcite-components/2.5.1/calcite.esm.js"></script>
    <link rel="stylesheet" type="text/css" href="https://js.arcgis.com/calcite-components/2.5.1/calcite.css" />
  </head>

  <body>
    <calcite-shell>
      <calcite-panel heading="Using esri/request">
        <calcite-block
          heading="Enter a URL:"
          description='Enter a server URL to the input box below, then click the "Make Request" button to send a request.'
          open
        >
          <calcite-input-text
            id="input-url"
            placeholder="https://services.arcgisonline.com/arcgis/rest/services/World_Topo_Map/MapServer"
            value="https://services.arcgisonline.com/arcgis/rest/services/World_Topo_Map/MapServer"
            required
          ></calcite-input-text>
          <calcite-button id="request-button" style="padding-top: 5px">Make Request</calcite-button>
          <calcite-label style="padding-top: 10px" scale="l"
            >Server response from the request:
            <calcite-text-area
              id="results-text-area"
              placeholder="Request response will show here.."
              read-only
              rows="25"
            ></calcite-text-area>
          </calcite-label>
        </calcite-block>
      </calcite-panel>
    </calcite-shell>
    <calcite-alert id="alert" kind="danger" icon label="Danger alert" auto-close>
      <div slot="title">Enter a valid URL.</div>
    </calcite-alert>
    <script>
      require(["esri/request"], (esriRequest) => {
        const requestButton = document.getElementById("request-button");
        const urlInput = document.getElementById("input-url");
        const textArea = document.getElementById("results-text-area");
        const alert = document.getElementById("alert");
        let url;

        let sStates = ['Arizona', 'California', 'Nevada']
        let promiseStates = [];
        let sResults = '';
        
        requestButton.addEventListener("click", () => {
            //var variable = null;
            for (let j = 0; j < sStates.length; j++) {
              let thestate = sStates[j];
              const aDef = new Promise((resolve, reject) => {
                get_results(thestate, resolve, reject);
              });
              promiseStates.push(aDef);
            }
            
            Promise.all(promiseStates).then((values) => {
                console.log("in Promise.all");
                console.log(values);
                textArea.value += sResults;
            });
        });
        
        async function get_results(state, pRes, pRej) {
            console.log(state);
          let getinfo =
            "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/2/query?f=json&Where=STATE_NAME='";
          params = state;
          params += "'&outFields=NAME,HISPANIC";

          getinfo = getinfo + encodeURI(params);

          var options = {
            query: {
              f: "json",
            },
            responseType: "json",
          };
          console.log(getinfo);
          console.log()
          await esriRequest(getinfo, options).then((response) => {
            console.log("in esriRequest");
            console.log(response.data);
            sResults += response.data.fields["Name"];
            return pRes(response);
          })
            .catch(function (error) {
		            console.error("esriRequest Error: " + error);
		            textArea.value = error;
                return pRej(error);
	          });
        }
        
        

      });
    </script>
  </body>
</html>

 (oh, and it uses your original function for calling the REST service). 

JeffreyWilkerson
Occasional Contributor III

Oh, and look at the Console for the results. I just used the Esri sample for promises, but I cleaned most of it out and never got the textArea to work well. But the data's viewable in the console. 

0 Kudos
LefterisKoumis
Occasional Contributor III

Thank you Jeff. However it does not do the intended purpose. I wanted each iteration to go through the esri request and do the calculations based on the response before it proceeds to the next iteration. The script provided, it goes through the sStates loop and then execute all promises in parallel (Promise.all). As I mentioned before, I wanted each iteration to run in series to grab the variable at #27 and uses at #6 before it proceeds to the next iteration.

0 Kudos
ReneRubalcava
Frequent Contributor

If you make your loop async, I think you can get the behavior you're looking for.

https://codepen.io/odoe/pen/MWRWwax?editors=0010

If I was doing this from scratch, I might set it up a bit different, so that the variable was a returned value and not set on each iteration, but this seems to work.

LefterisKoumis
Occasional Contributor III

Thank you. Huge oversight on my part not to add another async/wait at the beginning.

0 Kudos