Jump to content

Using async/await


gw1500se
 Share

Recommended Posts

I have a function that uses fetch which is an asynchronous function. After reading on how to wait for the result I am unable to figure out how to  do the wait. Here is my script:

function requests(url) {
	fetch(url)
  		.then(response => {
    	// indicates whether the response is successful (status code 200-299) or not
    		if (!response.ok) {
                  throw new Error(`Request failed with status ${response.status}`)
    		}
                return response.json()
 		 })
 		 .then(data => {
    		    console.log(data.num_results)
   		    return(data.results)
 		 })
  		.catch(error => console.log("Auto_select: "+error))
}
var json_formatted_str, obj;
console.log("Getting page data");
json_formatted_str = requests("https://worker.mturk.com/projects.json");
console.log(json_formatted_str);

Can someone help me set this function up so it does not return until the fetch completes? TIA.

Link to comment
Share on other sites

Thanks. I saw that but I can't figure out how to do that in the context of my function and how to return the value:

async function requests(url) {
	const x=await fetch(url)
  		.then(response => {
    	// indicates whether the response is successful (status code 200-299) or not
    		if (!response.ok) {
                  throw new Error(`Request failed with status ${response.status}`)
    		}
                await return response.json()
 		 })
 		 .then(data => {
    		    console.log(data.num_results)
   		    return(data.results)
 		 })
  		.catch(error => console.log("Auto_select: "+error))
}
var json_formatted_str, obj;
console.log("Getting page data");
json_formatted_str = requests("https://worker.mturk.com/projects.json");
console.log(json_formatted_str);

Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules

Link to comment
Share on other sites

At the top-level you do not use awaits, and instead the Javascript runtime will handle them. As in, if it finds a dangling Promise somewhere then it will go ahead and let it run to completion. That's why you can call fetch().then().catch() without having to await anything yourself.
You also do not need to use await inside the .then(). It's okay to return a Promise, which is what .json() will return. That Promise it returns then gets "unwrapped" before the next .then kicks in.

In other words, why are you adding async/await in the first place? Is there a reason, or are you just trying to learn more about it?

Link to comment
Share on other sites

Usually you'll want to use async/await or then/catch, not both.  You can do both, but it makes the code a bit more confusing.  await can only be used in async functions, not in the global scope.  In the global scope you're stuck using then/catch.

async function requests(url) {
	const response = await fetch(url);
	if (!response.ok){
		throw new Error(`Request failed with status ${response.status}`)
	}

	const data = await response.json();
	console.log(data.num_results)
	return data.results;
}

console.log("Getting page data");

//If this were in another async function, you could await.  If it's top level, you have to then.
requests("https://worker.mturk.com/projects.json").then(results => {
	console.log(results);
}).catch(error => console.log("Auto_select: "+error));

 

Link to comment
Share on other sites

Posted (edited)
9 minutes ago, requinix said:

In other words, why are you adding async/await in the first place? Is there a reason, or are you just trying to learn more about it?

That's a confusing statement. My understanding is that the fetch returns immediately since it is an async process.

This whole thing is confusing and a tough nut for me to crack.

Edited by gw1500se
Link to comment
Share on other sites

    /* Handle General Errors in Fetch */
    const handleErrors = function (response) {
        if (!response.ok) {
            throw (response.status + ' : ' + response.statusText);
        }
        return response.json();
    };    

    /* Success function utilizing FETCH */
    const UISuccess = (data) => {
        console.log(data); // Parsed Data coming back from Ajax
    };

    const UIError = (error) => {
        console.log("Database Table did not load", error);
    };
   
    /* create FETCH request */
    const createRequest = (url, succeed, fail) => {
        fetch(url)
            .then((response) => handleErrors(response))
            .then((data) => succeed(data))
            .catch((error) => fail(error));
    };


    /* Call the createRequest function const NameOfFunction () => {} */
    createRequest(requestUrl, UISuccess, UIError);

I found breaking down FETCH in separate function calls to be easier to understand. Maybe this will help?

Link to comment
Share on other sites

4 hours ago, gw1500se said:

That's a confusing statement. My understanding is that the fetch returns immediately since it is an async process.

This whole thing is confusing and a tough nut for me to crack.

fetch returns a Promise immediately. That does not mean the Promise has been resolved yet.

Link to comment
Share on other sites

Posted (edited)
12 hours ago, kicken said:

Usually you'll want to use async/await or then/catch, not both.  You can do both, but it makes the code a bit more confusing.  await can only be used in async functions, not in the global scope.  In the global scope you're stuck using then/catch.

 

I tried using your code and it seems to work. However, what I am getting is not quite what I need. It appears that 'results' shows 2 arrays. I want to put the 2nd array into a variable for processing. When I do something like 'hits=results[1]' I don't really get the same thing in the array that is logged. This is the result of the 'console.log(results)' (unexpanded):

>Array(0)

>Array(53)

How do I put the 2nd array into a variable I can process as an array?

I tried 'hits=results[1]' but I get the first element of the 2nd array.

Edited by gw1500se
Link to comment
Share on other sites

Time to do a reset. This may need to be a new thread. Here is my current code:

async function requests(url) {
   const response = await fetch(url);
   if (!response.ok) {
        throw new Error(`Request failed with status ${response.status}`)
   }         
    const data = await response.json();
    return data.results;
}

console.log("Getting page data");
hits=requests("https://worker.mturk.com/projects.json").then(results => {
   return(results);
}).catch(error => console.log("auto_select: "+error));
console.log(hits);

The console log now shows this (unexpanded):

\/Promise

   >[[Prototype]]: Promise

   >[[PromiseState]]: "fulfilled"

   >[[PromiseResult]]: Array(55)

This doesn't seem like normal json but I need to get that array into a variable.

Link to comment
Share on other sites

Anything you want to do with the results of a promise must be done either after using await or within the .then method. 

Just ignore the return value from your requests function and do whatever work you want to do within the .then method.

 

Link to comment
Share on other sites

All I want to do is get that array outside of the function. The processing I need to to on it is going to be too much. Plus the function would then have to be recursive as the data within the array will require additional fetches.

Link to comment
Share on other sites

Think of promises like events, they work similarly in that there's an unknown delay you have to account for.  What you seem to be trying to do so far would be the equivalent of

var whichKey;
input.addEventListener('keypress', (e) => { whichKey = e.keyCode; });
console.log('You typed: ' + whichKey);

The code isn't going to wait around for the key press before moving onto the console.log statement.  It'll just move on, log that you typed nothing and then run the key press code later whenever you actually press a key.

It's the same with your fetch statement here.  The code isn't going to just hang around waiting for the fetch to complete before moving on.  As such, any code which depends on the results of the fetch must be integrated into the .then callback function.

await lets you code as if everything just stopped and waited, but must be combined with async and cannot be used at the global scope.  Without knowing more about the rest of your code, it's hard to say what your solution needs to be.  If this fetch request is being made from some event handler, then all you'd likely need to do is make your event handler function async and then await the result. 
 

window.addEventListener('DOMContentLoaded', async (e) => {
  const data = await requests('/');
  console.log(data);
});

If that's not possible for some reason, you might need to do more restructuring of the code.

Link to comment
Share on other sites

Posted (edited)

I think we are missing my question which is why I thought a new thread is needed. I have the data outside of the function but I don't recognize the format it is in so I don't know how to extract that array I need. The variable (hits) is typeof 'obj'.

Edited by gw1500se
Link to comment
Share on other sites

9 minutes ago, gw1500se said:

I have the data outside of the function

No, you don't according to what you've posted so far.  Your hits variable is just the promise object that you get as a result of calling fetch().  The data you want is provided to the callback you pass into the .then function of that promise. Your code return(results) in the .then function does not return that value into hits, it pass that value into the next .then function if there was one.  Without one it just does nothing.

If you're still stuck, or think I'm still not understanding the problem I think you'll need to provide more code/context.

Link to comment
Share on other sites

4 minutes ago, gw1500se said:

Are you saying I need another .then to return the results of the fetch?

No, you just need to do whatever you want to do with the results inside the .then function rather than try and return the data back outside of the function somehow.

 

Link to comment
Share on other sites

Posted (edited)

OK, although that seems impractical given what I need to do with the array contents. I think the function then becomes superfluous if I can't get anything outside of it. We no longer have a .then. I don't know where that would go given the current code.

Edited by gw1500se
Link to comment
Share on other sites

I don't know what you need to do with the results, that's why I suggested posting more code.

It might be that the way the await/async syntax works that is tripping you up.  await is only usable within a function marked as async.  Any function that is marked as asyc automatically returns a Promise object.  Even if it looks like you're returning some specific value, you're not.  Whatever value you return from the function ends up being the result of the promise which is given to any attached .then handlers (or as the result of await).  So this code:

async function requests(url) {
   const response = await fetch(url);
   if (!response.ok) {
        throw new Error(`Request failed with status ${response.status}`)
   }         
    const data = await response.json();
    return data.results;
}

Does not return your result data like you might expect by looking at it.  That code is just a "modern" / "cleaned up" version of the more traditional:

function requests(url) {
	return fetch(url).then(function(response){
		if (!response.ok) {
			throw new Error(`Request failed with status ${response.status}`)
		}
		
		return response.json();
	}).then(function(data){
		return data.results;
	});
}

That should be easier to see that what the requests() function returns is a Promise, not data.results.  So you'd then take that returned promise and add your .then() handler to process whatever the results are.

requests('/').then(function(results){
	for (let item of results){
		console.log(item);
	}
});

 

Link to comment
Share on other sites

Posted (edited)

First this is a Chrome extension. I haven't developed any more code yet since I am not sure what elements will be returned. The results of that fetch produces a large array of json elements. Depending on filters I have to build to analyze each array element, I need to issue another fetch for specific elements. After processing that, if it meets certain criteria, I will pass that information to another extension's API. The process will need to continue to be repeated every 'n' time periods. Perhaps I need an alternative method for getting my data but fetch seems like the only way to get data from a URL in javascript.

Edited by gw1500se
Link to comment
Share on other sites

Wrap your code in a single async function and call it.  Then you can code in a more traditional style using await.  When it comes time to do the "repeat every N time periods" part you can then pass that function to a call to setInterval.  Example:

function requests(url) {
	return fetch(url).then(function(response){
		if (!response.ok) {
			throw new Error(`Request failed with status ${response.status}`)
		}
		
		return response.json();
	});
}

async function myExtension(){
	const data = await requests('https://worker.mturk.com/projects.json');
	for (let item of data.results[1])
	{
		console.log(item);
	}
}

myExtension();

// When you're ready to repeat:
//setInterval(myExtension, 5000);

 

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.