Dave.Q
Dave.Q

Reputation: 399

How to return the value from an async function

I have an async function that I would like to return a value from and then use.

Within the updatedStyleSheet function, I'd like to return the updated_css so that I can use it somewhere else.

async function updatedStyleSheet() {
  const res = await fetch("./prism.css");
  const orig_css = await res.text();
  let updated_css = orig_css;

  const regexp = /(?:var\(--)[a-zA-z\-]*(?:\))/g;
  let cssVars = orig_css.matchAll(regexp);
  cssVars = Array.from(cssVars).flat();

  for (const v of cssVars) {
     updated_css = updated_css.replace(v,   colors[v.slice(6, -1)]);
   };

// return updated_css ?
}

If I call this function, I get a Promise object. Rightfully so, since async functions always return a promise.

Could I return the updated_css within the updatedStyleSheet function and do something like this after?

const newSheet = updatedStyleSheet().then(css => css)

Then use the newSheet variable wherever in my script?

End Goal

Take the updated_css that contains the text contents of my CSS and use it as an href value so the user can download the stylesheet.

Edit

I attempted to add an event handler so that all the selected color values would be saved in the stylesheet once the user saves it, but it doesn't seem to work. I know it is still an issue with my understanding of promises as a whole.

What I did.

const updatedStyleSheet = async () => {
  const res = await fetch("./themes/prism.css");
  const orig_css = await res.text();
  let updated_css = orig_css;

  const regexp = /(?:var\(--)[a-zA-z\-]*(?:\))/g;
  let cssVars = orig_css.matchAll(regexp);
  cssVars = Array.from(cssVars).flat();
  console.log(cssVars)

  for await (const variable of cssVars) {
    const trimmedVar = variable.slice(6, -1)
    const styles = getComputedStyle(document.documentElement)
    const value = String(styles.getPropertyValue(`--${trimmedVar}`)).trim()

    updated_css = updated_css.replace(variable, value);
  }
  console.log(updated_css)

  return updated_css
}

const main = async () => {
  const downloadBtn = document.getElementById('download-btn')
  downloadBtn.addEventListener('click', () => {
    const updated_css = updatedStyleSheet()
    downloadBtn.setAttribute('href', 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(updated_css))
    downloadBtn.setAttribute('download', 'prism-theme.css')
  })
}

main()

I can't await the updated_css because it falls into the callback of the click event, which is a new function.

Then I did the following thinking it would work since it was top level.

const downloadBtn = document.getElementById('download-btn')
downloadBtn.addEventListener('click', async () => {
  const updated_css = await updatedStyleSheet()
  downloadBtn.setAttribute('href', 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(updated_css))
  downloadBtn.setAttribute('download', 'prism-theme.css')
})

That gave me the following error TypeError: NetworkError when attempting to fetch resource.

Upvotes: 1

Views: 492

Answers (3)

ranieribt
ranieribt

Reputation: 1306

Yes. Since you're already using async/await, just create a top level async function, say main, and use the returned updated_css content after awating the promise to resolve:

async main() {
  const updated_css = await updatedStyleSheet()
  // use css as you please
}

// run program
main()

Don't mind the cost of one more function call.

Upvotes: 1

Martin Heralecký
Martin Heralecký

Reputation: 5789

If you need to wait for an async function to return a value (so that you can use this value afterwards), use the await keyword:

const newSheet = await updatedStyleSheet()

However, mind that awaiting blocks the execution until the function returns. This might not be the behaviour you want.

Upvotes: 0

Raphael Castro
Raphael Castro

Reputation: 1148

I would collapse the promise by using the .then() function EX:

var bar = updatedStyleSheet()
bar.then(updated_css=>{
//logic goes here
});

Upvotes: 1

Related Questions