DEV Community

Cover image for Async/await can still surprise you... A LOT!
Charles Assunção
Charles Assunção

Posted on

Async/await can still surprise you... A LOT!

I love technology ❤, and the fact that it doesn't matter how much we know there will always be something to amaze us. Today a friend of mine (@Rafael_Toscano) showed me something that my instant reaction was like this:

Cat Surprised

He shared with me an article from the V8 blog about "Faster async functions and promises." and among all sort of exciting things, one captured my attention in a way that I could only think "This can't be real, I have to test it."

It regards the async/await behavior and the fact you can use async/await with any "thenable" function. What does it mean? Any object which has a ".then" method can be used with async/await.

In the article, he gives the following example:

class Sleep {
  constructor(timeout) {
    this.timeout = timeout;
  }
  then(resolve, reject) {
    const startTime = Date.now();
    setTimeout(() => resolve(Date.now() - startTime),
               this.timeout);
  }
}

(async () => {
  const actualTime = await new Sleep(1000);
  console.log(actualTime);
})();

Mother Of God

Yes, please tell me I am not the only one whose mind was blown by seeing that.

I think this helps us understand a little bit more about async/await functions and the possibilities of things we can do in our code. But also it comes with great responsibility, please don't replace simple promises everywhere by this only because it feels nice.

Only use it if you can find exceptional use cases, and if you do, please share it with us in the comments. I do love to hear about it! I'm considering if to implement a "retry strategy" wouldn't be a good opportunity for this use as following the idea in the bellow code.

const ServerMock = {
  count: 0,
  getData() {
    if (this.count === 2) {
      return Promise.resolve([{ name: "SomeName" }]);
    } else {
      this.count++;
      return Promise.reject("Error");
    }
  }
};

function fetchData(limit, time) {
  return {
    then(resolve, reject) {
      const retry = setInterval(async () => {
        limit--;
        try {
          console.log("Trying....");
          const data = await ServerMock.getData();
          if (data) {
            console.log("Resolve");
            clearInterval(retry);
            resolve(data);
          }
        } catch {
          if (limit === 0) {
            clearInterval(retry);
            reject("Limit Reached");
          }
        }
      }, time);
    }
  };
}

(async () => {
  try {
    const result = await fetchData(3, 1000);
    console.log(result);
  } catch (err) {
    console.log(err);
  }
})();

Let me know what you think about it in the comments.

Top comments (6)

Collapse
 
joelnet profile image
JavaScript Joel

Hello Charles,

I wanted to show a couple of additional ways of writing Sleep

First, you can use util.promisify on the setTimeout method:

const { promisify } = require('util');

const sleep = promisify(setTimeout);

(async () => {
  await sleep(1000);
  console.log('Hello');
})();

You can also use a function like this:

const sleep = timeout =>
  new Promise(resolve => setTimeout(resolve, timeout));

(async () => {
  await sleep(1000);
  console.log('Hello');
})();

Also, one neat thing I like to do is create a generic retry function. That way I can use it on any function that returns a Promise!

It would look something like this:

const serverMock = {
  count: 0,
  getData() {
    if (this.count === 2) {
      return Promise.resolve([{ name: "SomeName" }]);
    } else {
      this.count++;
      return Promise.reject("Error");
    }
  }
};

const retry = (attempts, action) => {
  const promise = action()
  return attempts > 0
    ? promise.catch(() => retry(attempts - 1, action))
    : promise
}

retry(2, () => serverMock.getData())

You can even do the same with the time limit feature!

Cheers! 🍻

Collapse
 
assuncaocharles profile image
Charles Assunção

Really nice Joel,

regarding the sleep/retry one amazing tool to make it even more simple is rxjs :D

learnrxjs.io/learn-rxjs/operators/...

Collapse
 
joelnet profile image
JavaScript Joel

Ahh the under appreciated rxjs. Such an amazing tool!

Collapse
 
frankdspeed profile image
Frank Lemanschik

you should not even show that in this post and it is a anti pattern x10 a so called 10 x anti pattern because it breaks Environment Interop. you should use new Promise((res,rej)=>/* .... */) Because when i want to read your code or search in your code i do not know what is going on. Also you make it hard to Polyfill. I do not like what i see here overall.

For any one if you find such code replace it with nativ Promises or a Promise Polyfill that is known so that your code can get reused by others if you only code for your own use and you or your team are the only people that use that it is ok to do so. As long as you do not get affraid by the diffrent behavior of throw new Error() inside the then able.

Collapse
 
assuncaocharles profile image
Charles Assunção

Dude, it's just a curiosity. Relax and take easy, not suggesting usage in real life application in any moment.

Collapse
 
frankdspeed profile image
Frank Lemanschik

sorry i maintain projects like rollup and when i see such code i get fear that i need to support such stuff and i do not want to do that. But i see i will need to do it anyway