DEV Community

Arden de Raaij
Arden de Raaij

Posted on • Originally published at arden.nl

All about Promises and async / await

Recently I revisited Wes Bos his ES6 for everyone course and made some notes on the Promise and async / await modules. These notes got a little out of hand and now they've turned into this huge blogpost. This is in no way meant to be a transcript of ES6 for everyone and I consulted many resources for this article, which you can find at the bottom of this post.

If I got something wrong or missed something important, feel free to correct! You can find this post in my front-end notes github repo.

Promises

ECMAScript 2015 (ES6) brought us the Promise, a native object which act as a proxy (or temporary value) for a value that isn't known yet. Promises allow you to create handlers that deal with the eventual success or failure of an asynchronous action. In this post I'll tell you about all the ways to use promises, how to roll your own Promise functions, how to combine and chain promises and how to make the Promise api even better with async / await. But first we're going to back to the absolute basics: Synchronous and Asynchronous code.

Synchronous / Asynchronous

I'm absolutely sure that most of you can skip this part, but if you do like a small reminder of what kind of asynchronous behaviour we deal with in JavaScript, keep on reading.

When you create functions or objects that immediately return a value, JavaScript seems to be synchronous. It looks like each line is processed sequentially and returns values before the next process starts running.

console.log('this will fire first');
console.log('this will fire second');
console.log('this will fire last');
Enter fullscreen mode Exit fullscreen mode

The above code will return exactly what you'd expect and logs all these lines of text to the console in order.

However, when one of these processes takes longer to return a value than the others we can see that the behaviour is in fact, asynchronous. In the following example we'll add a setTimeout around the second console.log to clearly see what happens.

console.log('this will fire first');
setTimeout(() => {
    console.log('this will fire second');
}, 500);
console.log('this will fire last');
Enter fullscreen mode Exit fullscreen mode

Now the logging in the console is out of order because the next line will not wait for the previous to finish. To recap:

  • 'This will fire first' is logged to the console immediately
  • The setTimeout function is started with 500ms on the timer and will not return a value for that amount of time.
  • 'this will fire last' is logged to the console because it doesn't wait for the result of the setTimeout function.
  • After the 500ms, 'this will fire second' is logged to the console.

In our example we obviously add the delay ourselves and as long as we know what the timeout value is, we can deal with the delay. But in many cases we don't know when something is going to return a value, like when we're fetching data or deal with a complicated process that takes long before returning a value.

We used to deal with this by using callbacks. A function would take in a callback function which would be called whenever the time consuming process returned something.

function wait(ms, cb) {
    setTimeout(function() {
        console.log(`done after ${ms}ms`);
        cb();
    }, ms);
};

wait(1000, function() {
    console.log('here\'s our callback function');
});
Enter fullscreen mode Exit fullscreen mode

This doesn't look to bad. But what if we have another function with callback that needs to be called within our callback, and another one within that function? It's going to get complicated real quickly and it's what we commonly refer to as callback hell.

wait(300, function() {
    wait(600, function() {
        wait(500, function() {
            wait(400, function() {
                console.log('here\'s our final callback function');
            });
        });
    });
});
Enter fullscreen mode Exit fullscreen mode

All the indenting makes makes the code very hard to read. There were ways around this, but that's not important anymore, because we've got the Promise!

Promise - Introduction

The Promise is a native object which acts as a temporary value for a value that isn't known yet. Promises allow you to create handlers that deal with the eventual success or failure of an asynchronous action

Native promises

Fetch

Before we'll start rolling our own promises, let's have a look on how to work with a promise that is already available in your browser, natively! Since a couple of years fetch has been my go-to api to request data. It's very clean, easy to remember and to handle. If you didn't use ajax requests with jQuery, you might remember XMLHttpRequest, which wasn't a pretty way to get data. Well, no more copy-pasting because you'll have fetch memorized in no-time. I promise.

console.log(fetch('https://api.github.com/users/aderaaij'));
Enter fullscreen mode Exit fullscreen mode

The code above is still asynchronous. We're not capturing the result of the promise, we're just logging the object itself. The result should look something like Promise {<pending>}. This shows you that the user variable is indeed a promise and that the status on the moment of calling console.log(user) was pending. That's cool and all, but we want to see some data!

To check on the status of a promise, we can tag on a .then method.

fetch('https://api.github.com/users/aderaaij')
    .then(data => console.log(data));
Enter fullscreen mode Exit fullscreen mode

In this little piece of code we're fetching user data from github and we directly log it when it comes back successful! That was insanely easy, wasn't it? Now the data we've got is still 'raw'. fetch can be used to retrieve all kinds of data so it doesn't just assume your data is JSON. Luckily we can convert it to JSON data with the json() method, which returns a promise as well.

Whenever the .then method returns a value, whether a Promise or not, you can tag another .then method right onto it. When you return a value, it's returned as a promise that immediately resolves: Promise.resolve(val). When you return a Promise, the following .next will be called when the Promise is actually resolved.

fetch('https://api.github.com/users/aderaaij')
    .then(data => data.json())
    .then(data => console.log(data));
Enter fullscreen mode Exit fullscreen mode

As the fetch function returns a promise, we call then on it to transform the data. To turn the data into usable JSON, we call .json() on it. As .json() returns a promise as well, we tag on another .then and can do whatever we want with the transformed data 🎉. You can see how this chaining can be useful if you need to combine multiple functions which are depending on data from functions that might or might not immediately return a value.

But what if there's an error?

fetch('api.github.com/users/aderaaij')
    .then(data => data.json())
    .then(data => console.log(data));
Enter fullscreen mode Exit fullscreen mode

In the script above we forgot https:// so the browser is now looking for this api in my local files. If you run this, the error should say Uncaught (in promise). This means that we haven't used the catch method we can tag on to the promised returned by the fetch api.

fetch('api.github.com/users/aderaaij')
    .then(data => data.json())
    .then(data => console.log(data))
    .catch(err => console.error('oh noes:', err));
Enter fullscreen mode Exit fullscreen mode

In this example we've added our catch method and we throw an explicit error. The console message should be a bit more informative now.

Finally, we also have the finally method. Finally returns a promise when the original promise is either resolved or rejected. It's a method you can call when you want something to happen regardless if the function resolves or not. It could be useful to reset a state based on whether the promise has run and finished already. The browser support is still lacking but it could be useful in the future.

fetch('https://api.github.com/users/aderaaij')
    .then(data => data.json())
    .then(data => console.log(data))
    .catch(err => console.error('oh noes:', err))
    .finally(() => console.log('finally'));
Enter fullscreen mode Exit fullscreen mode

getUserMedia

Another cool in-browser promise is the MediaDevices.getUserMedia() method which prompts the user to use a media input like their webcam or microphone and produces a medium stream. Of course this function can't run without user permission and has to wait for the user to accept to start doing something.

const video = document.querySelector('video');
navigator.mediaDevices.getUserMedia({ video: true })
    .then(mediaStream => {
        video.srcObject = mediaStream;
        video.load();
        video.play();
    })
    .catch(err => console.error(Error("user said no 😡")))
Enter fullscreen mode Exit fullscreen mode

In the above example mediaDevices.getUserMedia({ video: true }) a prompt to ask user permission to get webcam access. When the user either accepts or rejects, the promise resolves or reject. When we accept, we set the mediaStream as sourceObject for our video element, load up the video and play it.

Building your own promises

There are plenty of user cases when we'd want to make our own promises and here I'll show you how to do just that.

The base of a Promise looks like this:

const p = new Promise((resolve, reject) => {

});
Enter fullscreen mode Exit fullscreen mode
const p = new Promise((resolve, reject) => {
    resolve('yay');
});
Enter fullscreen mode Exit fullscreen mode

It takes in one function called the executor ☠️ with the arguments of resolved and reject. The idea being that whatever the promise returns either resolves and returns some kind of value, or results in an error.

We can execute all kinds of code within our new promise and whenever we've got what we want to return and / or reject, we call resolve and reject.

const p = new Promise((resolve, reject) => {
    resolve('This is awesome!');
});

p.then(console.log)
Enter fullscreen mode Exit fullscreen mode

In this case we immediately resolve the value 'This is awesome!' When we apply the then method to our promise we immediately get back the value that was resolved.

When you replace resolve with reject, you'd see we will get an uncaught (in promise) error, with the same message. We can also catch this error and give it a little bit more meaning.

const p = new Promise((resolve, reject) => {
    reject(Error('This is an error'));
});

p
    .then(console.log)
    .catch(err => console.error(err));
Enter fullscreen mode Exit fullscreen mode

Now the error is caught and logged. Because we wrapped the reject message in an Error object, we get a lot more information on what might be the problem.

In many cases we would like to return both a reject and a resolve in our promise. For example, you could return a promise in a function that takes in an argument, and reject or resolve based on the argument value.


function isTonyStark(name) {
    return new Promise((resolve, reject) => {
        if (name === 'Tony') {
            resolve(`Welcome ${name}`);
        } else {
            reject(Error('Danger, Will Robinson, danger!'));
        }
    });
}

isTonyStark('Tony')
    .then(console.log)
    .catch(err => console.error(err));
Enter fullscreen mode Exit fullscreen mode

Chaining promises

Promises are very convenient when dealing with multiple processes which will not return something at the same time, especially when those processes might be dependent on each other. With promises you can control the flow and make sure that you won't execute the next function until the data from the previous one has returned.

So let's sketch a common scenario where you've got two sets of data: A list of movies and a list of heroes. The sets of data come from a database call, so you don't exactly know when you'll have it available. You want to make a function that returns both the movie information as some extra info on the main hero.

const movies = [
    { title: 'Thor 3, Ragnarok', company: 'Marvel', hero: 'Thor', id: 1 },
    { title: 'Black Panther', company: 'Marvel', hero: 'Black Panther', id: 2 },
    { title: 'Wonder Woman', company: 'DC', hero: 'Wonder Woman', id: 3 },
];

const heroes = [
    { name: 'Thor', team: 'Avengers' },
    { name: 'Black Panther', team: 'Avengers' },
    { name: 'Wonder Woman', team: 'Justice League', actor: 'Gal Gadot' },
];
Enter fullscreen mode Exit fullscreen mode

So the first thing we're going to do, is roll our own promise. We want to be able to get a movie by ID, so here we go:

function getMovieByID(id) {
    // We can immediately return a promise in our function, this is how we pass arguments
    return new Promise((resolve, reject) => {
        // Find the movie based on the movie ID
        const movie = movies.find(movie => movie.id === id);
        if (movie) {
            resolve(movie); // Resolve if we've got a movie
        } else {
            reject(Error('oh noes, no movie found'));
        }
    })
}

getMovieByID(3)
    .then(console.log);
Enter fullscreen mode Exit fullscreen mode

And there you have it, the first part of our solution.

For our next step, we need to make another promise so we can chain it onto our getMovieById. When the data is returned, we immediately want to start using it and turn it into something useful.

Our hydrateData function takes in the data object which is returned in the then handler from the getMovieById promise. It then returns the new Promise we're making.

function hydrateData(data) {
    // Return a new promise
    return new Promise((reject, resolve) => {
        // Find the hero by comparing the `hero` value in the `data` object to `hero.name`
        const info = heroes.find(hero => data.hero === hero.name);
        if (info) {
            data.hero = info; // Assigning info to data.hero (replacing the original `hero` value which was just a string)
            resolve(data);
        } else {
            reject(Error('have no heroes'));
        }
    });
}

getMovieByID(3)
    .then(data => hydrateData(data))
    .then((data) => {
        console.log(data);
    });
Enter fullscreen mode Exit fullscreen mode

And now everything together:

function getMovieById(id) {
    return new Promise((resolve, reject) => {
        const movie = movies.find(movie => movie.id === id);
        if (movie) {
            resolve(movie);
        } else {
            reject(Error('Movie not found'));
        }
    });
}

function hydrateData(data) {
    return new Promise((resolve, reject) => {
        const heroInfo = heroes.find(hero => data.hero === hero.name);
        console.log(heroInfo);
        if (heroInfo) {
            data.hero = heroInfo;
            resolve(data);
        } else {
            reject(Error('oh noe error'));
        }
    });
}

getMovieById(3)
    .then(data => hydrateData(data))
    .then((data) => {
        console.log(data);
    });
Enter fullscreen mode Exit fullscreen mode

Promise.all - Multiple promises

In some cases you want to return multiple promises and wait for all of them to resolve before doing something with that data. In that case you can use Promise.all. .all takes in an array of iterables (promises included) and waits for all of those to be resolved before returning values.

function printThor() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve({
                name: 'Thor',
                bff: 'The Hulk',
                team: 'Avengers',
            });
        }, 500);
    });
}

function printQuotes() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(['This drink... I like it!', 'I need a horse!']);
        }, 1000);
    });
}

Promise
    .all([printThor(), printQuotes()])
    .then(([thor, quote]) => console.log(thor, quote));
Enter fullscreen mode Exit fullscreen mode

In this example we've got two promises, printThor returns after 500ms, printQuotes after 1000ms. We feed them to Promise.all in an array, and call .then. This returns data when both promises are resolved. To make live easy we destructure both values right in the arrow function arguments.

But what if you fetch data from an array and still need to transform that data to useful JSON? In that case you might want to return yet another Promise.all. This time with a .map function which maps over the responses and returns .json(). Since we're using .map() which returns an array and .json() which returns a Promise, we basically return an array with promises.

const dog = fetch('https://dog.ceo/api/breeds/image/random');
const dev = fetch('https://api.github.com/users/aderaaij');

Promise
    .all([dog, dev])
    .then(res => Promise.all(res.map(r => r.json())))
    .then(data => console.log(data));
Enter fullscreen mode Exit fullscreen mode

Promises vs Callbacks

If you remember that piece we wrote on

function wait(ms) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(console.log(`waited for ${ms}ms`));
        }, ms);
    });
}

wait(300)
    .then(res => wait(500))
    .then(res => wait(1000))
    .then(res => wait(700))
    .then(res => wait(300))
    .then(res => wait(900))
    .catch(err => console.error(err));
Enter fullscreen mode Exit fullscreen mode

This makes our code a lot more flat and thus readable.

Async / Await

At the core, Async / Await is build on top of promises.
In order to async / await anything, you'll need a function that returns a promise.
Await always needs to be called within a function marked with async. There is no top-level await.

function wait(ms) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(console.log(`waited for ${ms}ms`));
        }, ms);
    });
}

const go = async () => {
    await wait(600);
    await wait(1200);
    await wait(1800);
}
go();
Enter fullscreen mode Exit fullscreen mode

In this case we take the exact same Promise we created in the previous code block, and call it in a function marked with async. Just by adding await infront of your function, your code will run synchronously and each wait function waits untill the previous one has resolved. This API is even clearer than the .then method and there seems to be some performance gains as well. At the moment of writing async / await is supported by most browsers.

You can also stick the returned values of a function into a variable:

function wait(ms) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if(ms > 500) {
                resolve(`waited for ${ms}ms`);
            } else {
                reject(Error(`you should wait longer than ${ms}ms!`));
            }
        }, ms);
    });
}

const go = async () => {
    const res1 = await wait(600);
    console.log(res1);
    const res2 = await wait(1000);
    console.log(res2);
    const res3 = await wait(1400);
    console.log(res3);
};

go();
Enter fullscreen mode Exit fullscreen mode

In the example above, the result of the first promise gets logged after 600ms while the results of the second and third promises will be logged together after 3600ms.

Error handling

Handling errors with async code blocks is a little bit awkward. You can surround your await statements with a try and catch block like this:

function wait(ms) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if(ms > 500) {
                resolve(`waited for ${ms}ms`);
            } else {
                reject(Error(`you should wait longer than ${ms}ms!`));
            }
        }, ms);
    });
}

const go = async () => {
    try {
        const res1 = await wait(600);
        console.log(res1);
        const res2 = await wait(600);
        console.log(res2);
        const res3 = await wait(300);
        console.log(res3);
        const res4 = await wait(600);
        console.log(res4);
    } catch (err) {
        console.error('something went wrong...', err);
    }
}

go();
Enter fullscreen mode Exit fullscreen mode
function wait(ms) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if(ms > 500) {
                resolve(`waited for ${ms}ms`);
            } else {
                reject(Error(`you should wait longer than ${ms}ms!`));
            }
        }, ms);
    });
}

const go = async () => {
    try {
        const res1 = await wait(600);
        console.log(res1);
        const res2 = await wait(600);
        console.log(res2);
        const res3 = await wait(300);
        console.log(res3);
        const res4 = await wait(600);
        console.log(res4);
    } catch (err) {
        console.error('something went wrong...', err);
    }
}

go();
Enter fullscreen mode Exit fullscreen mode

In the example above we've wrapped all of the await promises in our try block, and if one is rejected we catch that error in our catch block.

You could also make a sort of 'Higher order component' which would wrap the go function and would catch all the errors. This is something I totally got from Wes Bos and you should check his talk on async/await in which he goes further into this.

function wait(ms) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if(ms > 500) {
                resolve(`waited for ${ms}ms`);
            } else {
                reject(Error(`you should wait longer than ${ms}ms!`));
            }
        }, ms);
    });
}

// First we make a function that takes in our async function as an argument
const catchErrors = (fn) => {
    // And return a function
    return function() {
        // Which returns our async function, which is a promse on which we can call `.catch`
        return fn().catch((err) => {
            console.error('uhoh', err);
        });
    };
};

const go = async () => {
    const res1 = await wait(600);
    console.log(res1);
    const res2 = await wait(600);
    console.log(res2);
    const res3 = await wait(300);
    console.log(res3);
    const res4 = await wait(600);
    console.log(res4);
}

const wrappedFunc = catchErrors(go);
Enter fullscreen mode Exit fullscreen mode

This way we don't have to write a different try and catch block for each promise. You only want to do that when you need to resolve a lot of promises though, otherwise you might be better off writing your try / catch block and write a bit more specific error messages.

Awaiting multiple promises

You need to be careful with await and multiple promises, it's not a replacement for Promise.all. Adding an await statement before your promises makes your code truly synchronous, so if you resolve two fetch promises with await, the one won't start fetching data before the other is finished. In a lot of cases you'd like them to fire off at the same time and wait for the result of both to come back.

async function getDogs() {
    // Store the promise in a variable
    const dog1 = fetch('https://dog.ceo/api/breeds/image/random');
    const dog2 = fetch('https://dog.ceo/api/breeds/image/random');

    const results = await Promise.all([dog1, dog2]); // Wait until both promises are ready
    // Array destructure, await our res.json() promises
    const [mut1, mut2] = await Promise.all(results.map(res => res.json()));
    console.log(mut1, mut2);
}
Enter fullscreen mode Exit fullscreen mode

If we have multiple promises we can even map over them and return them in a Promise.all.

async function getDogs(breeds) {
    const promises = breeds.map((breed) => {
        return fetch(`https://dog.ceo/api/breed/${breed}/images/random`);
    });

    const results = await Promise.all(promises);
    const data = await Promise.all(results.map(r => r.json()));
    console.log(data);
}
getDogs(['husky', 'malamute', 'terrier']);
Enter fullscreen mode Exit fullscreen mode

Fin

That sums it up for now! If you have any questions or remarks, please don't hesitate to comment or contact me!

Resource list

Top comments (19)

Collapse
 
nickytonline profile image
Nick Taylor

Arden, thanks for the writeup.

In regards to the section “Awaiting multiple promises”, I would reword “Adding an await statement before your promises makes your code truly synchronous,” as it’s not correct. The await keyword allows you to write asynchronous code in a synchronous fashion, but the code running is still asynchronous. When there’s two awaits in a row, all that’s happening is the first asynchronous action is executed and when it’s complete (provided there's no error), only then does the next awaitable action run asynchronously as well.

When using Promise.all I prefer to destructure the results. I find it reads better, e.g. const [dog1Result, dog2Result] = await Promise.all([dog1, dog2]);

For those new to Promises, to complement the resource list in this post, I highly recommend practicing ES6 Katas over at es6katas.org. They have a great section on Promises.

Collapse
 
ardennl profile image
Arden de Raaij

Oh thank you so much, that's great feedback which I'll implement ASAP. Those katas are awesome by the way. I've been going at them for 30 minutes now and am just devouring them. I feel confident now 💪

Collapse
 
jjjjcccjjf profile image
endan • Edited

I have been struggling with Promises for about a week (or two?) now. Last night, I just read your blog post before I sleep and I can't sleep! Because I know I have already fixed the problem in my head. And now, it's 6:26am here. I opened my laptop, made some tea, applied what I learned and BAM! It worked on the first try! I knew it! 😊 I knew this post would be enlightening! Btw, here's my code! ✨

exports.register = function (req, res) {
  var user = new Users(req.body)

  const hash = Users.hashPassword(user.password)
  const activationCode = Users.generateActivationCode() // REVIEW: Refactor?? hmmm

  Promise.all([activationCode, hash])
  .then(function ([activationCode, hash]) {
    user.activationCode = activationCode
    user.password = hash
    save()
  })
  .catch((err) => console.error(err))

  function save () {
    user.save(function (err, doc) {
      if (err) {
        res.send(err)
      } else {
        user.sendActivationCode()
        res.json(doc)
      }
    })
  }
}

Many many thanks! I have read this blog earlier and it turns out your post was a very nice addition reading to this!

Collapse
 
ardennl profile image
Arden de Raaij

Seriously this the best way to start my week with, thank you!! I'm so glad to hear you fixed your problem with my article 🙌. The code is looking good, what are you working on?

Collapse
 
jjjjcccjjf profile image
endan • Edited

Thank you! 😁 Your article is really good!

Ah, I've been working on a REST API side project (to be consumed by a React app, which I have yet to learn 😆). My weapon of choice is really PHP, but I started to learn Node intermittently since December. My main reason of doing so is Electron and React Native and as well as React. It's blows me away to use just one language across all stacks! And as well as PWAs (service workers operating on JS).

I have one last question though. In Javascript for Dummies blog, there's a code snippet that I'll paste below:

/* ES6 */
const isMomHappy = true;

// Promise
const willIGetNewPhone = new Promise(
    (resolve, reject) => { // fat arrow
        if (isMomHappy) {
            const phone = {
                brand: 'Samsung',
                color: 'black'
            };
            resolve(phone);
        } else {
            const reason = new Error('mom is not happy');
            reject(reason);
        }

    }
);

const showOff = function (phone) {
    const message = 'Hey friend, I have a new ' +
                phone.color + ' ' + phone.brand + ' phone';
    return Promise.resolve(message);
};

// call our promise
const askMom = function () {
    willIGetNewPhone
        .then(showOff)
        .then(fulfilled => console.log(fulfilled)) // fat arrow
        .catch(error => console.log(error.message)); // fat arrow
};

askMom();

As you can notice here, the author used 2 different syntax in returning a promise. The first one is the (resolve, reject) => arrow function style, and the second one is Promise.resolve(message). My question is, is there a "right" way of doing promises? Should one just keep it consistent or let it adjust based on one's code?

Or is it not important at all?

Also, if you noticed in my code, I didn't use fat arrow functions because I'm not too comfortable with them (yet). Is it "okay" to do this or it's really just preference and consistency?

Sorry for many questions, and thanks again! ☺

Thread Thread
 
ardennl profile image
Arden de Raaij

Cool!! Don't hesitate to blog about your progress, I've yet to make an API in node but I'm sure I will need to do something like that soon.

I totally agree, JavaScript at both the client and server side is pretty damn amazing isn't it.

About the different Promise syntax examples, that is actually a really good question! The difference between the two are not that big, besides that the first one offers a clear way to reject a promise, and the second one does not. If you'd return undefined in showOff, that would probably throw a wrench in the process. Apparently there's another subtle difference but I have yet to understand this: stackoverflow.com/questions/267112... 😂

Personally I'm all for consistency. I like the fat arrow function and got used to how they work with this, but if you're not totally comfortable with them yet, a normal function calls is just as good!!

Thread Thread
 
jjjjcccjjf profile image
endan

Thanks a lot!! 😁

Collapse
 
aravindballa profile image
Aravind Balla • Edited

An amazing line from @wesbos podcast syntaxfm

await(await(fetch('https://api.github.com/users/wesbos'))).json();
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jrop profile image
Jonathan Apodaca

I personally prefer the following form:

await fetch('https://api.github.com/users/wesbos').then(r => r.json())
Collapse
 
ardennl profile image
Arden de Raaij

That's a gem! I do feel like not everyone would get what's going on here on first glance though!

Collapse
 
jjjjcccjjf profile image
endan

I appreciate your effort to write something like this. It's really helpful for beginners like me. Thank you! I love how you write as well. 😁

Collapse
 
jjjjcccjjf profile image
endan • Edited

Sorry to ask @ardennl but can you talk about Transpilers (Babel, etc) next? 😊

Collapse
 
ardennl profile image
Arden de Raaij

Thanks for the kind words! And definitely, that is a great idea! I was thinking about topics for my next blog post, this just might be it.

Thread Thread
 
jjjjcccjjf profile image
endan

Alright! I'm super excited! Thank you so much!

Collapse
 
developius profile image
Finnian Anderson

This is awesome! I've learnt a few different tricks here, my favourite being using .catch() on an async function, I didn't know you could do that. Thanks 💯

Collapse
 
ardennl profile image
Arden de Raaij • Edited

Thank you! There's so much written about promises I wasn't sure I should publish this, but I'm glat I did! Also got so much info out of it myself 🙌

Collapse
 
rajeshroyal profile image
Rajesh Royal

Danger, Will Robinson, danger! waiting for next season

Collapse
 
n13 profile image
Nikolaus

Very good and very clear, thank you

Collapse
 
ardennl profile image
Arden de Raaij

Thank you, appreciate the feedback!