A real-life example of making a custom Promise in JavaScript/TypeSctipt
Tags: webdev, javascript, typescript
Published: 2021-09-27 20:08:15

It feels like we've completely moved from callbacks to _Promises_ and _async/await_ in the JavaScript world. So much, that almost every library and framework provides async versions of their functions. And the logic usually goes like this: 'I see _async_ -> I type _await_ and mark my function _async_ as well -> done!'. I got so used to it, so I started to forget how to create and use my own _Promises_. 

But life always finds a way to remind you that old habits die hard, especially if you're talking about browsers APIs.

So, if you're like me and need a little reminder on how to make custom _Promises_, I hope you'll find this post helpful.

## Creating html images.
So here is the problem. We need to create an Image element using a browser's API and set its source to a dataUrl of some picture. At first, I wrote something like this.
```typescript
function drawImage(dataUrl: string) {
  const image = new Image();
  image.src = dataUrl;
  return image;
};
```
Looks great, but there is a problem here. When we set `image.src` it's not instantly loaded. Even if a source is a data string, and not an URL to an external resource, it still takes time. So when an image is returned from the function, the is no guarantee that the data is already there.

Unfortunately, if you want to wait for the loading to be done, you can't do something like this.
```typescript
function drawImage(dataUrl: string) {
  const image = new Image();
  await image.src = dataUrl; // STOP! THIS IS ILLEGAL!
  return image;
};
```
The only way is to set an event handler. But what do we put here?
```typescript
function drawImage(dataUrl: string) {
  const image = new Image();
  image.addEventListener('load', () => {
    // ???
  });
  image.src = dataUrl;
  return image;
};
```
If we were still in 2010, we would solve this by having another argument for a callback function.
```typescript
function drawImage(dataUrl: string, onDone: () => void) {
  const image = new Image();
  image.addEventListener('load', () => {
    onDone();
  });
  return image;
};
```
But wait, it's 2021. Cool kids don't do that anymore. What we need, is to make our function return something awaitable. And there is nothing more awaitable than a _Promise_. Here is the _Promise_ constructor
```typescript
function Promise<T>(
  executor: (
    resolve: (value: T) => void, 
    reject: (reason?: any) => void
  ) => void
)
```
Looks a bit scary, but you just need to get to know it better. This is how you usually call it.
```typescript
const promise = new Promise((resolve, reject) => {
  // Lalala, do some work
  // ....
  if (we_are_good_and_got_the_result) {
    resolve(result);
  } else {
    reject(error);
  }
})
```
So you pass a function in the constructor. A function that has 2 arguments, which are also functions. Call them `resolve` and `reject`. In that function we do some work. If everything went well, just call `resolve` with the result. Otherwise call reject with an error object.
The thing that you passed into the `resolve` is gonna be returned as a _Promise_ result when it resolves.

Then you can use this new object.
```typescript
promise.then((result) => {
  // Use the result
}).catch(error => {
  // Oh no there was an error
})
```
And, what's even better, you can also do this.
```typescript
const result = await promise;
```

Let's apply what we learned to our problem.
```typescript
function drawImage(dataUrl: string) {
  const promise = new Promise<HTMLImageElement>((resolve, reject) => {
    const image = new Image();
    image.addEventListener('load', () => {
      resolve(image);
    }, { once: true }); // We don't want this handler to run more than once.
    image.src = dataUrl;
  });
  return promise;
}
```
Then you can call your new function with `await`.
```typescript
const image = await drawImage('data');
```
And that's it. We've done it!

## Bonus example for those who can't wait.
The is one more example I want to show you. It's actually quite useful to be able to just wait for a set amount of time. Sadly, there is not `wait()` or `sleep()` function in JavaScript. But with our new powers we can make our one. Here is a quick one-liner for you guys to try and figure out as an exercise (I feel like a college teacher by saying this lol)
```typescript
const wait = async (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
```

## Show me more please Senpai.
This article is actually an extended version of an excerpt of my latest post. You're welcome to check it out if you want more ;). {% link https://dev.to/nordicbeaver/can-a-machine-be-a-css-artist-4691 %}