Promises and Static Values

By  on  

Async can throw a real wrench into the cogs of our programming workflows, all despite the fact that async is the modern JavaScript pattern. While async/await helps, there's sometimes confusion about the way to have a single function that returns a value whether it exists or needs a Promise to retrieve.

The key thing to remember is that functions declared as async automatically return a Promise, so you don't need explicity return the existing content with Promise.resolve(content):

async function getValueOrFetch(ojbOrInfo) {
  // If the value exists, immediately return it
  if(ojbOrInfo) {
    return ojbOrInfo;
  }
  // Return the promise-based info
  return asyncFunctionToGetInfo(ojbOrInfo);
}

Let's look at a real life example: returning cached contents instead of doing a fetch call to retrieve them:

const cache = {
  /* url: content */
};

// Async function that returns cached content or retrieves fresh content
async function getInfo(url) {
  // Check for value in cache
  if (cache[url]) {
    // Return the content, no need for Promise.resolve
    return cache[url];
  }
  // Get the content fresh
  const content = await fetch("https://www.facebook.com").then(r => r.text());
  cache[url] = content;
  return content;
}

My main goal with this post is making you understand that return Promise.resolve(data) isn't needed with async functions -- you can simply return the value and it will be wrapped in a promise!

Recent Features

  • By
    fetch API

    One of the worst kept secrets about AJAX on the web is that the underlying API for it, XMLHttpRequest, wasn't really made for what we've been using it for.  We've done well to create elegant APIs around XHR but we know we can do better.  Our effort to...

  • By
    9 More Mind-Blowing WebGL Demos

    With Firefox OS, asm.js, and the push for browser performance improvements, canvas and WebGL technologies are opening a world of possibilities.  I featured 9 Mind-Blowing Canvas Demos and then took it up a level with 9 Mind-Blowing WebGL Demos, but I want to outdo...

Incredible Demos

  • By
    CSS Sprites

    The idea of CSS sprites is pretty genius. For those of you who don't know the idea of a sprite, a sprite is basically multiple graphics compiled into one image. The advantages of using sprites are: Fewer images for the browser to download, which means...

  • By
    CSS Custom Cursors

    Remember the Web 1.0 days where you had to customize your site in every way possible?  You abused the scrollbars in Internet Explorer, of course, but the most popular external service I can remember was CometCursor.  CometCursor let you create and use loads of custom cursors for...

Discussion

  1. Teddy

    If the function is async, shouldn’t it automatically wrap any return value in a promise?

  2. Gordon Smith

    Also that cache “pattern” has a race condition if its called twice in quick succession (it may end up calling the server twice) as there is a window of opportunity during the “await” for the server call.

    The pattern I tend use roughly (untested code) like this:

    const cache = {
        /* url: content */
    };
    
    // Async function that returns cached content or retrieves fresh content
    async function getInfo(url) {
        // Check for value in cache
        if (!cache[url]) {
            cache[url] = fetch("https://www.facebook.com").then(r => r.text());
        }
        return cache[url];
    }
    

    There isn’t any real need for an “await” inside the function…

  3. TIm

    Isn’t async function always returning a promise?

  4. Sam

    Is there any difference between this syntax and Promise.resolve or, in an aync function, just returning the value?

  5. Actually,

    the last function does not need to return Promises…

    It’s declared as async which means whatever you return will become a promise…

    So you could simplify it as:

    // Async function that returns cached content or retrieves fresh content
    async function getInfo(url) {
      // Check for value in cache
      if (cache[url]) {
        // Return a value from cache with a quick promise
        return cache[url];
      }
      // Get the content fresh
      const content = await fetch("https://www.facebook.com").then(r => r.text());
      cache[url] = content;
      return content;
    }
    
  6. I am pretty sure that when we are declaring an async function we do not need to return a new Promise.
    If the function is not declared as an async function the example would be precise.
    But as we declare an async function we can just return the cached object directly and without needing to wrap it in a new Promise or use Promise.resolve becaus every async function will return a promise and wait on every await clause.

  7. I tend to wrap my entire promise-returning functions in one new Promise. In my mind, it reduces confusion. The example code you gave becomes something like this:

    async function getInfo(url) {
      return new Promise((resolve, reject) => {
        // Check for value in cache
        if (cache[url]) {
          // Return a value from cache with a quick promise
          resolve(cache[url]);
        }
        // Get the content fresh
        const content = await fetch(url).then(r => r.text());
        cache[url] = content;
        resolve(content);
      });
    }
    • Ben C

      You actually don’t need to do that, and nor should you, because that’s exactly what the async keyword does, so you’re just adding redundant complexity, which bloats code and is likely to lead to bugs, etc.

    • Wesley de A Queiroz

      I totally agree with Ben C.
      All async functions are already wrapped at runtime in a promise.
      Their return value is passed to the resolve function and their throw value are passed to reject.
      Just reading MdN docs should clarify that.

      And in fact your example will throw at parse time bec await is not available on the promise constructor function because it is not an async function, only it’s parent function.

      As far as differentiating an async function from a common function every good code editor will provide typing information and will show the return type as a promise.
      And the declaration prefix (async) is clear enough to tell that you are dealing with promises.

      In the development world we should always avoid to re-do any work that the platform amd/or ecosystem already does for us, this avoid code bloating and ensures a more common communication between people working together on a project.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!