Asynchronous TypeScript Code
Tags: typescript, async, promise
Published: 2020-04-11 13:00:20

The synchronous code is implemented sequentially. but Async is parallel and notifies on completion, failure or progress.
<strong>Async Types<strong>
* Callbacks
* Promises
* Async/Await

<strong>1. Callbacks</strong>

Common places to use Callbacks
* Timers and intervals
* Modal with response
* Http request

Steps to for Callbacks function
1. You pass the callback to another function
2. The function does its work
3. The function executes your callback
4. The callback now has the data and start to proceed

**Sync Callbacks**

here the callback function executed synchronously like when using forEach in the following example

```typescript
export function syncCallBack(data: string[]) {
  data.forEach((val) => {
    console.log(val);
  })
}
```

**Async Callbacks**

you can use setTimeout setInterval to execute callBacks Async.
```typescript
export function getMessage(timeout: number, showMsgCallBack: (msg: string) => void) {
  setTimeout(() => {
    showMsgCallBack('Data returned');
  }, timeout);
}

getMessage(1000, (msg) => {
  console.log(msg);
})
```
**Callbacks with http request**

it is common to use callbacks with Http requests, like the following example, use  callbacks with Jquery ajax requests but always try to consider the error callback

```typescript
const getUserBooks = function (
  userId: number,
  callback: (data: { id: number, name: string }[]) => void,
  callbackError?: (msg?: string) => void
) {
  $.ajax({
    url: `api/users/${userId}/books`,
    type: 'GET',
    dataType: 'json',
    contentType: 'application/json',
    success: function (res: { id: number, name: string }[]) {
      callback(res);
    },
    error: function (msg: string) {
      if (callbackError) {
        callbackError(msg);
      }
    }
  });
}
```


___

<strong>2. Promises</strong>

The promise represents the eventual completion or failure of an async operation and its resulting value.

**The Key Terms**
* FulFilled =>  The promise succeeded, often with resolve or Promise.resolve
* Rejected => The promise failed, often with reject or Promise.reject
* Pending => The promise is not fulfilled nor rejected yet.
* Settled => The promise has either been fulfilled or rejected.

**Promise constructor**

```typescript
let promise = new Promise<User[]>((resolve, reject) => {
  //execute your code
  //call resolve if success
  //call reject if failed
})
```
The function passed to <code>new Promise</code> is called the executor. When <code>new Promise</code> is created, the executor runs automatically.

The promise object returned by the constructor has the following internal properties (We can’t directly access them)

* state - initially "pending", then changes to "resolved" when success and "rejected" when failed
* result - initially undefined, then changes to the value when resolve called or the error when reject called.

A promise that is either resolved or rejected is called "settled" as opposed to an initially "pending" promise.
The executer function should call one resolve or one reject and any further calls for resolve or reject will be ignored.

```typescript
let promise = new Promise<User[]>((resolve, reject) => {
  //execute your code
  resolve(['data1', 'data2']);
  resolve(['']); //ignored
  reject(Error('Error')) //ignored
})
```

**Promise Consumers**

* then => has two arguments the first for success and the second for reject
* catch => called when an error occurred
* finally => runs when the promise is settled (success or failed) don't know whether the promise is successful or not.

let's see an example
```typescript
class User {
  id: number;
  name: string;
}
function getUsers(): User[] {
  return [{id: 1, name: 'Mina'}, {id: 2, name: 'John'}, {id: 3, name: 'Narouz'}]
}
let promise = new Promise<User[]>((resolve, reject) => {
  setTimeout(()=> {
    let users = getUsers();
    if (users.length > 0) {
      resolve(users);
    } else {
      //it is recommended to use Error object with reject
      reject(Error('Error, No Users found'));
    }
  }, 1000)
})

promise
.then((users: User[]) => {
  users.forEach(user=> {
    console.log(user.name);
  })
})
.catch((err: Error) => {
  console.log(err.message);
})
.finally(() => {
  console.log('finished');
})
```

**Promise.all**

Is a method returns a single Promise that fulfills when all of the promises passed as an iterable have been fulfilled

in some cases, you need to do execute behavior after multiple promises have done so instead of doing nested callbacks the better to use Promise.all as in the following example

```typescript
let p1 = Promise.resolve(22);
let p2 = Promise.resolve('Data returned');
Promise.all([p1, p2]).then((res: [number, string]) => {
  let p1Res = res[0]; //22
  let p2Res = res[0]; //Data returned
})
```

but If any of the passed-in promises reject, Promise.all asynchronously rejects with the value of the promise that rejected, whether or not the other promises have resolved.

```typescript
let p3 = Promise.all([1,2,3, Promise.reject(555)]);
// Promise { <state>: "rejected", <reason>: 555 }
```

It is possible to change this behavior by handling possible rejections:

```typescript
var p1 = new Promise((resolve, reject) => { 
  setTimeout(() => resolve('p1 success result'), 1000); 
}); 

var p2 = new Promise((resolve, reject) => {
  reject(new Error('p2 Error'));
});

Promise.all([
  p1.catch(error => { return error }),
  p2.catch(error => { return error }),
]).then((values: [string, string]) => { 
  console.log(values[0]) // "p1 success result"
  console.error(values[1]) // "p2 Error"
})
```

___

<strong>3. Async/Await</strong>

Async functions make it possible to treat functions returning promises as if they were synchronous. It can be used in place of or with promises.

```typescript
async function func(): Promise<string> {

  let promise = new Promise<string>((resolve, reject) => {
    setTimeout(() => resolve("Got data"), 1000)
  });

  let result = await promise; // wait until the promise resolves

  return result; // "Got data"
}

func().then(res => console.log(res));
```

<code>async</code> before function means functions always returns a promise. <code>await</code> only works inside <code>async</code> functions and makes JavaScript wait until that promise settles and returns its result.

**Error Handling**

It is recommended to use try/catch/finally with async/await

```typescript
async function getData() {
  try {
    let promise = new Promise<string>((resolve, reject) => {
      setTimeout(() => reject(Error('Errors!')), 1000)
    });
    const res = await promise;
  } catch (error) {
    console.log(error.message);
  } finally {
    console.log('done');
  }
}
```

**Promise.all with async/await**

You can use Promise.all with async/await functions as the following

```typescript
async function getAllDataForUser(userId: number) {
  let response = await fetch(`/api/users/${userId}`);
  let user = await response.json();
  const booksResponse = await fetch(`/api/users/${userId}/books`);
  const ordersResponse = await fetch(`/api/users/${userId}/orders`);
  const [books, orders] = await Promise.all([
    booksResponse.json(),
    ordersResponse.json(),
  ]);
  user.books = books;
  user.orders = orders;
  return user;
}
```

**for await of**

If you need to Make one async call at a time you can use <code>for await of</code> instead of <code>Promise.all</code> as in the following example I will replace <code>Promise.all</code> in the previous example

```typescript
async function getAllDataForUser(userId: number) {
  let response = await fetch(`/api/users/${userId}`);
  let user = await response.json();
  const booksResponse = await fetch(`/api/users/${userId}/books`);
  const ordersResponse = await fetch(`/api/users/${userId}/orders`);
  let asyncCalls = [booksResponse.json(), ordersResponse.json()];
  let index = 0;
  for await (let res of asyncCalls) {
    if (index == 0) {
      user.books = res;
    } else {
      user.orders = res;
    }
    index++;
  }
  return user;
}
```