Monthly Archives: February 2020

Comparing Callbacks, Promises and Async Await in TypeScript

By John Papa

Comparing Callbacks, Promises and Async Await in TypeScript

How do callbacks, promises and async/await compare to each other? This article shows the same scenario using each of these three techniques so you can see the differences and choose which appeals most to you.

The code uses TypeScript, but can easily be adapted to JavaScript.

Learn more about this code in my course Creating Asynchronous TypeScript Code on Pluralsight.

This article shows three different techniques to get an object graph of a hero with that hero’s orders and account rep.

The scenario for these examples are that there is a set of heroes. Each hero has to shop, so they make orders. And each hero has a dedicated account rep for their orders. Heroes are like customers, if that helps 😄

Callbacks

When writing callbacks we end up with a series of neted calls. This is easy to see when we look a the code below as it all tends to drift to the right. This drifting is also known as the “Pyramid of Doom”.

The code below gets a hero by the hero’s email. Then it gets the orders for the hero and merges them into the hero object. Then it gets the account repo for the hero and merges that data into the hero object. Finally, it returns the hero with all of the orders and account rep data.

const getHeroTreeCallback = function(
  email: string,
  callback: Callback<Hero>,
  callbackError?: CallbackError,
) {
  getHeroCallback(
    email,
    hero => {
      getOrdersCallback(
        hero.id,
        orders => {
          hero.orders = orders;
          getAccountRepCallback(
            hero.id,
            accountRep => {
              hero.accountRep = accountRep;
              callback(hero);
            },
            error => callbackError(error),
          );
        },
        error => callbackError(error),
      );
    },
    error => callbackError(error),
  );
};

Individual Callbacks

The getHeroTeeCallback function calls nested functions. You can see these in the following code example.

There are three functions here. Each gets the Hero, the Hero’s orders, and the Hero’s account reps, respectively. Notice that each follows a pattern of using axios to get the data over http, and invokes the callback or callbackError function based on whether the code worked or encountered an error.

const getHeroCallback = function(
  email: string,
  callback: Callback<Hero>,
  callbackError?: CallbackError,
) {
  axios
    .get<Hero[]>(`${apiUrl}/heroes?email=${email}`)
    .then((response: AxiosResponse<Hero[]>) => {
      const data = parseList<Hero>(response);
      const hero = data[0];
      callback(hero);
    })
    .catch((error: AxiosError) => {
      console.error(`Developer Error: Async Data Error: ${error.message}`);
      callbackError(`Oh no! We're unable to fetch the Hero`);
    });
};

const getOrdersCallback = function(
  heroId: number,
  callback: Callback<Order[]>,
  callbackError?: CallbackError,
) {
  const url = heroId ? `${apiUrl}/orders/${heroId}` : `${apiUrl}/orders`;
  axios
    .get(url)
    .then((response: AxiosResponse<Order[]>) => {
      const orders = parseList<Order>(response);
      callback(orders);
    })
    .catch((error: AxiosError) => {
      console.error(`Developer Error: Async Data Error: ${error.message}`);
      callbackError(`Oh no! We're unable to fetch the Orders`);
    });
};

const getAccountRepCallback = function(
  heroId: number,
  callback: Callback<AccountRepresentative>,
  callbackError?: CallbackError,
) {
  const url = `${apiUrl}/accountreps/${heroId}`;
  axios
    .get(url)
    .then((response: AxiosResponse<AccountRepresentative>) => {
      const list = parseList<AccountRepresentative>(response);
      const accountRep = list[0];
      callback(accountRep);
    })
    .catch((error: AxiosError) => {
      console.error(`Developer Error: Async Data Error: ${error.message}`);
      callbackError(`Oh no! We're unable to fetch the Account Rep`);
    });
};

Promises

Promises do have some indentation to the right, like callbacks. However it tends to not be as extreme. The promise is called to get the Hero and then the orders and the account reps are retrieve at the same time using Promise.all.

This is different than the allback technique where each call is made one at a time. Promise.all allows you to take the hero data and use it to make two calls: one for orders and one for account reps. When both have returned their responses, the code moves in to the next then.

The final step is to merge the orders and account repo data into the Hero.

Notice also, that the nested functions are inside of the getHeroTreeProimise function. This allows the those functions to access the hero variable in the outer function. Otherwise, you’d want to pass the hero around. I prefer this type of closure technique, as it gives those functions context of where they should work (on a hero).

const getHeroTreePromise = function(searchEmail: string) {
  let hero: Hero;

  // Level 1 - Get the hero record
  return (
    getHeroPromise(searchEmail)
      // Level 2 - Set the hero, and pass it on
      .then((h: Hero) => {
        hero = h;
        return h;
      })
      // Level 3 - Get the orders and account reps
      .then((hero: Hero) => Promise.all([getOrders(hero), getAccountRep(hero)]))
      // Extract the orders and account reps and put them on their respective Hero objects
      .then((result: [Order[], AccountRepresentative]) => mergeData(result))
  );

  function getOrders(h: Hero): Promise<Order[]> {
    hero = h;
    return h ? getOrdersPromise(h.id) : undefined;
  }

  function getAccountRep(h: Hero): Promise<AccountRepresentative> {
    hero = h;
    return h ? getAccountRepPromise(h.id) : undefined;
  }

  function mergeData(result: [Order[], AccountRepresentative]): Hero {
    const [orders, accountRep] = result;
    if (orders) {
      hero.orders = orders;
    }
    if (accountRep) {
      hero.accountRep = accountRep;
    }
    return hero;
  }
};

Async Await

The async await technique gets the same data, but follows a much more “do this then do that” flow. The code flows line by line, just like syncrhonous code flows.

First you get the hero. Then you get the orders and account rep. Notice that you can use the Promise.all combined with the async await. This is really helpful as it allows you to make boths calls at the same time, but still “await” their response. Then those responses are merged into the hero object.

This code feels the cleanest to me. Less lines and arguably easier to read.

const getHeroTreeAsync = async function(email: string) {
  const hero = await getHeroAsync(email);
  if (!hero) return;

  const [orders, accountRep] = await Promise.all([
    getOrdersAsync(hero.id),
    getAccountRepAsync(hero.id),
  ]);
  hero.orders = orders;
  hero.accountRep = accountRep;
  return hero;
};

Nested Async Functions

The functions that the async await function getHeroTreeAsync calls are shown below. Here they use axios with the async and await keywords. The data is retrieved adn then returned.

const getHeroAsync = async function(email: string) {
  try {
    const response = await axios.get(`${apiUrl}/heroes?email=${email}`);
    const data = parseList<Hero>(response);
    const hero = data[0];
    return hero;
  } catch (error) {
    handleAxiosErrors(error, 'Hero');
  }
};

const getOrdersAsync = async function(heroId: number) {
  try {
    const response = await axios.get(`${apiUrl}/orders/${heroId}`);
    const data = parseList<Order>(response);
    return data;
  } catch (error) {
    handleAxiosErrors(error, 'Orders');
  }
};

const getAccountRepAsync = async function(heroId: number) {
  try {
    const response = await axios.get(`${apiUrl}/accountreps/${heroId}`);
    const data = parseList<AccountRepresentative>(response);
    return data[0];
  } catch (error) {
    handleAxiosErrors(error, 'Account Rep');
  }
};

Resources

You can learn more about these techniques fro these resources:

Source:: johnpapa

Debug Angular 9: Interacting with Components

By John Papa

Debug Angular 9: Interacting with Components

The Angular 9 Ivy runtime offers a new ng object for debugging Angular apps, when you run in Dev mode.

Inspect and Interact

Imagine that we have a parent component named HeroesComponent and a child component named HeroDetailComponent. We select a hero in the parent and we see the child. Now if we want modify the hero in the child and see when those changes get applied to the parent, we can do that through debugging in the browser console.

We’ll inspect the values step by step.

1 – Select the 5th hero

This will be Aslaug, the Warrior Queen.

2 – Select the input element

Using the Chrome development tools, select the input element for “Aslaug”.

Debug Angular 9: Interacting with Components

3 – Get references to the components

Set a reference to the HeroDetailComponent and its parent, HeroesComponent by entering the following commands into the Chrome developer tools console

// get the HeroDetailComponent
heroComp = ng.getContext($0)

// get the HeroesComponent
heroesComp = ng.getOwningComponent(heroComp)

Chrome developer tools expose $0 to identify the selected element. Then we use the ng debugging API to get the context of the selected element using ng.getContext($0). The context gets the HeroesDetailComponent for us.

We also want to get a reference to the parent component, HeroesComponent, so we can make sure the values we change are only sent from the child to the parent when the user presses the save button. We use the ng.getOwningComponent(heroComp) to grab this reference.

4 – Modify the component

Let’s modify the selected hero’s name.

// Change the hero's name
heroComp.editingHero.name = 'Madelyn'

We modify the component’s model. No changes are visible in the browser (yet).

5 – Compare the values

Now let’s compare the hero’s name in the child and parent components.

// Compare the hero in the child an parent components
heroComp.editingHero.name === heroesComp.heroes[4].name
// Should be false

We compare the parent and child components’ models’ values. These should be different, since we have modified the child and have not yet sent those changes to the parent.

6 – Save the changes

Let’s save the changes to the hero’s name, by calling the child component’s saveHero() function

// Save the changes to the hero
heroComp.saveHero()

We call the saveHero() function, simulating a user pressing the save button. This sends the child component’s Hero model to the parent, via an EventEmitter. Hmmm, no changes are visible in the browser (yet).

7 – Compare, Again

Once again, compare the hero’s name in the child and parent components

// Compare the hero in the child an parent components
heroComp.editingHero.name === heroesComp.heroes[4].name
// Should be true

We compare the values of the model in the child and parent components. This time they should be the same. But we notice that we still see the old values in the browser.

8 – Change Detection

Apply Angular’s change detection.

// Apply change detection
ng.applyChanges(heroComp)

Debug Angular 9: Interacting with Components

Now that we run the change detection, we see the changes update in the browser!

Learn More

Pretty cool! This is just one of the new features in Angular 9. To learn more, check out this article on 7 new features in Angular 9.

You can grab these great new Angular 9 tools here, too

  1. VS Code editor
  2. Angular Essentials Extension for VS Code
  3. Angular Language Service for VS Code

Source:: johnpapa

Debug Angular 9 in 60 Seconds

By John Papa

Debug Angular 9 in 60 Seconds

The Angular 9 Ivy runtime offers a new ng object for debugging Angular apps, when you run in Dev mode.

Let’s get an idea of what you can do.

Debug Angular 9 in 60 Seconds

This GIF shows the following steps:

  1. Run your Angular 9 app
  2. Open the Chrome developer tools
  3. Select the component’s element
  4. Type the following code in your console

Note that my angular app has a component with an array of heroes. Each hero has a name and a description. Change the code below to reflect your app.

// get the component you selected in the Elements panel
heroListComp = ng.getComponent($0); 

// Clone the first hero
before = {...heroListComp.heroes[0]};

// Change the first hero's name
heroListComp.heroes[0].name = 'Oliver';

// Clone the first hero, after your changes
after = {...heroListComp.heroes[0]};

// Display the before and after
console.table({before, after})

// Apply the changes (so you can see them in the View)
ng.applyChanges($0)

Note that we are using a few of the ng features here. the ng.getComponent() function gets the component associated with the element. This works for us here because we passed an element that is a component. It would return null if the element is not a component (like a div). The next article in this series will show some other techniques to get a component.

We also use the ng.applyChanges() function to tell Angular’s change detection to run.

Learn More

Pretty cool! This is just one of the new features in Angular 9. To learn more, check out this article on 7 new features in Angular 9.

You can grab these great new Angular 9 tools here, too

  1. VS Code editor
  2. Angular Essentials Extension for VS Code
  3. Angular Language Service for VS Code

Source:: johnpapa

Key takeaways when using Open API Specification 3 to document an ExpressJS API

By Adrian Matei

Recently I’ve taken the time to update the API documentation for bookmarks.dev-api.
Since it used the Swagger 2.0 (aka OAS 2), I decided to update to OpenAPI Specification (OAS) 3. In this post I will
highlight the main points about the process and documenting the API with OAS 3. Some points might still apply to the former OAS 2 (fka Swagger)
documentation, but they are worth mentioning since I hadn’t payed enough attention before and I find them useful.

You can find the OAS 3 specification on Github
and the result is available at bookmarks.dev/api/docs/

Here are the key takeaways.
Continue reading Key takeaways when using Open API Specification 3 to document an ExpressJS API