Introduction

In the last part, I used then to set the src attribute on the image tag directly.

// inside a click event handler
getImage(url)
  .then(result => {
    img.setAttribute('src', result)
  })
  .catch(err => console.log(err));

But sometimes it's useful to store the result of an awaited promise in a variable. For instance it might be necessary to query part of a database, store the result in a variable, query a different source, and then combine the results.

How do you do that? The above situation doesn't really work. What's needed is a way to let the async function return the result you want without necessarily chaining a then after it.

Setup

Let's keep everything the same as before...

HTML

The markup for this is (still) going to be very straightforward.

<div>
  <img src='' />
  <button>Get a random image</button>
</div>

Javascript

The first part here can be exactly the same as before with one exception. The event listener needs to be async. Feel free to look at the previous post if you need a refresher.

Event Listener

const img = document.querySelector('img')
const button = document.querySelector('button')
const div = document.querySelector('div')
const url = 'https://source.unsplash.com/random'

button.addEventListener('click', async () => {
  console.log('click');
});

async/await

Now it's time to create a function that will do what we want. We'll start with the same declaration as before.

const img = document.querySelector('img')
const button = document.querySelector('button')
const div = document.querySelector('div')
const url = 'https://source.unsplash.com/random'

const getImage = async (url) => {
  // handle awaiting the request and errors
}

button.addEventListener('click', async () => {
  console.log('click');
});

The difference is that instead of chaining then and catch to the function, everything needs to be handled inside it. To do that, first create a try/catch block.

const getImage = async (url) => {
  try {
    // handle a success
  } catch (err) {
    // catch errors and do something
  }
}

The next thing to do is allow the function to return something. Fortunately try/catch sets up it's own block context we can use to advantage.

const getImage = async (url) => {
  let result;
  try {
    
    // fetch the result of getting the image from the endpoint.
    const fetched = await fetch(url);
    
    // set the value of result equal to the src url
    result = fetched.src;
    
    // return the result
    return result;
  } catch (err) {
    // catch errors and do something
    console.error(`something bad happened -> ${err}`)
  }
}

Now when the button is clicked, the getImage function can be run and stored as a variable.

const img = document.querySelector('img')
const button = document.querySelector('button')
const div = document.querySelector('div')
const url = 'https://source.unsplash.com/random'

const getImage = async (url) => {
  let result;
  try {
    
    // fetch the result of getting the image from the endpoint.
    const fetched = await fetch(url);
    
    // set the value of result equal to the src url
    result = await fetched.src;
    
    // return the result
    return result;
  } catch (err) {
    // catch errors and do something
    console.error(`something bad happened -> ${err}`)
  }
}

button.addEventListener('click', async () => {
  const imgSrc = await getImage(url);
  
  // set the image's src attribute to the returned url
  img.setAttribute('src', imgSrc);
});

Handling failure

In this case, the error handling occurs inside the try/catch block so that's where you could do something to show the user a problem occurred.

Conclusion

I don't think there's too much more to say here!