DEV Community

Mina Marouf
Mina Marouf

Posted on

Asynchronous TypeScript Code

The synchronous code is implemented sequentially. but Async is parallel and notifies on completion, failure or progress.
Async Types

  • Callbacks
  • Promises
  • Async/Await

1. Callbacks

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

export function syncCallBack(data: string[]) {
  data.forEach((val) => {
    console.log(val);
  })
}
Enter fullscreen mode Exit fullscreen mode

Async Callbacks

you can use setTimeout setInterval to execute callBacks Async.

export function getMessage(timeout: number, showMsgCallBack: (msg: string) => void) {
  setTimeout(() => {
    showMsgCallBack('Data returned');
  }, timeout);
}

getMessage(1000, (msg) => {
  console.log(msg);
})
Enter fullscreen mode Exit fullscreen mode

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

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);
      }
    }
  });
}
Enter fullscreen mode Exit fullscreen mode

2. Promises

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

let promise = new Promise<User[]>((resolve, reject) => {
  //execute your code
  //call resolve if success
  //call reject if failed
})
Enter fullscreen mode Exit fullscreen mode

The function passed to new Promise is called the executor. When new Promise 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.

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

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

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');
})
Enter fullscreen mode Exit fullscreen mode

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

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
})
Enter fullscreen mode Exit fullscreen mode

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.

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

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

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"
})
Enter fullscreen mode Exit fullscreen mode

3. Async/Await

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.

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));
Enter fullscreen mode Exit fullscreen mode

async before function means functions always returns a promise. await only works inside async 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

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');
  }
}
Enter fullscreen mode Exit fullscreen mode

Promise.all with async/await

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

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;
}
Enter fullscreen mode Exit fullscreen mode

for await of

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

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;
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)