Asynchronous Programming with JavaScript/TypeScript
Tags: javascript, typescript, rxjs
Published: 2023-05-09 00:27:13

## Before Promises there were callbacks
Event handling before promises were introduced were done via callbacks.

__Before libraries and fetch there was XMLHttpRequest__

Early JavaScript involved usage of call back functions.  In the example below the developer needs to write an onreadystatechange function to handle when an asyncronous request was completed.  The structure below is similar analogous to the way we write event listeners for DOM elements.

```
var xhttp = new XMLHttpRequest();
// create a callback function which will be fulfilled when the request completes

xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
       // Typical action to be performed when the document is ready:
       document.getElementById("demo").innerHTML = xhttp.responseText;
    }
};
xhttp.open("GET", "filename", true);
xhttp.send();
```

__Abstracting away the XMLHttpRequest__

Various libraries created their owns mechanisms to make code like the above easier.  For example ExtJS, see https://www.extjs-tutorial.com/extjs/ajax-request-in-extjs4

```
        Ext.Ajax.request({
            url: '/api/students',
            method: 'GET',
            timeout: 60000,
            params:
            {
                id: 1 // loads student whose Id is 1
            },
            headers:
            {
                'Content-Type': 'application/json'
            },
            success: function (response) {
            
            },
            failure: function (response) {
                Ext.Msg.alert('Status', 'Request Failed.');

            }
        });
```

__Writing our own XMLHttpRequest Library__

We begin to see some improved code re-use and abstraction.

Can we make our own call-back style library to make XML Http Requests? Sure Why Not?

```

var Request = {
  
  get: function (url, success, failure) {
     var xhr = new XMLHttpRequest();
     
     xhr.onreadystatechange = function () {
       // 4 means we're done see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState
       if (this.readyState === 4) {
        if (this.statusCode >= 200 && this.statusCode < 300) {
           success(xhr.responseText);
        } else {
          failure(xhr.responesText);
        }
       }
     };
  };

  Request.get('/services/something',
    function (response) {
      console.log(response);
    },
    function (err) {
      console.error(err);
    }
};

```
__Callback Hell__

There some potential issues. When we start having to chain calls together, it gets messy.  The example below uses jQuery to write functions to call various REST api.

```

function getUser(success, failure) {
  $.get('services/user', function (response) {
    try {
      success(JSON.parse(response));
    } catch (err) {
      failure(response);
    }
  });
}

function getPermissions(userId, success, failure) {
  $.get('services/permissions/' + userId, function (response) {
    try {
      success(JSON.parse(response));
    } catch (err) {
      failure(err);
    }
  });
}

function getCurrentTime(success, failure) {
  $.get('services/currentTime', function (response) {
    try {
      success(JSON.parse(response));
    } catch (err) {
      failure(err);
    }
  });
}

// lets get the user
// check if they have permission
// if they do then get the time

function getTimeForUser(callback) {

  getUser(
    function (user) {
      getPermissions(user.userId,
        function (permissions) {
          if (permissions.canDoThing) {
            getCurrentTime(
              function (currentTime) {
                callback(null, currentTime);
              },
              function (error) {
                callback(error, null);
              }
            );
          }
        },
        function (error) {
          console.error('Failed to get permissions);
          callback(error, null);
        })
    },
    function (user) {
      console.error('Couldn\'t get the user');
      callback(error, null);
    });
}
```

## Promises

*https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise*
>The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.


It's easy to see the challenge of maintaining asynchronous codes using only callbacks.  Promises provide a new mechanism for transfering/representing asynchronous state.  Yes there are callbacks, but they are abstracted away in a variable we can use later.

### Basic structure
The basic structure of a promise is `then` and `catch`.  See example below:

```
function getThing() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('thing');
    }, 1000);
  });
}

const prom = getThing();

prom.then(
  // success handler
  function (thing) {
    console.log('I got the thing');
  },
  // second argument is failure, using this is considered an anti-pattern
  // see anti-patterns section for promises
  function (error) {
    console.error('I failed');
  }
// we can catch errors like this too
).catch((error) => {
  console.error('I failed');
})

```

### Promises make things cleaner ( a bit)
Promises return an object which represents a value to be eventually fulfilled.  It allows us to chain multiple asyncronous parts of code together more cleanly.  Promise chains follow the basic structure of `then`, `then`, `catch` 

__Basic Promise Chain Structure__

Chaining promises together allows us to more cleanly
write a sequence of asyncronous actions.

```
// then
Promise.resolve().then(
  // then
  () => Promise.resolve()).then(
  // catch
  () => Promise.reject('error')).catch(
      (err) => console.error('An error occured =>', err));
```

__A more useful promise chain example__

The code below represents requests to:
1. Get a User
2. Get Permissions for a user
3. Get the Current Time
4. Composing the above 3 functions together to make another function.

```
function getUser() {
  return fetch('services/user').
    then((response) => response.json());
}

function getPermissions(userId) {
  return fetch('services/permissions/' + userId).
    then((response) => response.json());
}

function getCurrentTime() {
  return fetch('services/currentTime').
    then((response) => response.json());
}

function getTimeForUser() {
  // get the user
  getUser().then(
    // get the permissions
    (user) => 
  // get permissions
  getPermissions(user.userId)).then(
      (permissions) => {
        if (permissions.canDoThing) {
          return true;
        } else {
          return false
        }
      }).then(
  // if they have permission
  (hasPermission) => {
    if (hasPermission) {
      return getCurrentTime();
    } else {
      return Promise.reject('doesn\'t have permission');
    }
  // catch - all
  }).catch((err) => {
        console.error('Failed to get time');
        return Promise.reject(err);
      });
}
```

### Promise Anti-Patterns
Although Promises clearly have the ability to improve things here are a few mistakes that are commonly made.
_see https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns#the-deferred-anti-pattern_

__The Deferred anti-pattern__

This is when you are creating promises for no reason when you already have a promise

```
function getThingsDeferredAntiPattern() {
  return new Promise((resolve, reject) => {
    fetch('/services/thing').then(
      (thing) => resolve(thing)).
      catch((err) => reject(err));
  });
}

function getThings() {
  return fetch('/services/thing');
}

```

__Nesting Promises__

This is often the fallback of developers who had previously worked with callbacks.

```
/**
 * NO
 */
getCurrentTimeForUserNested() {
  return getUser().then(
    (user) => {
      getPermissions(user.userId).then(
        (permissions) => {
          if (permissions.canDoThing) {
            return getCurrentTime();
          }
        })
    });
}

/**
 * Using promises properly, remember that a promise is an object
 * so we can chain then more neatly
 */
getCurrentTimeForUser() {
  return getUser().then(
    (user) => this.getPermissions(user.userId)).then(
    
    (permissions) => permissions.canDoThing).then(
    
    (canDoThing) => {
      if (canDoThing) {
        return getCurrentTime();
      } else {
        return Promise.reject(new Error('User doesn't have permission'));
      }
    });
}

```

__The .then(success, fail) anti-pattern__

It's considered bad practice to use the second argument of a `then` statement.  Using these tend to devolve into callback chains.

Using the above example belowL
```
getCurrentTimeForUser() {
  return getUser().then(
    (user) => {
      return getPermissions(user.userId).then(
        (permissions) => {
          if (permissions.canDoThing) {
            return this.getCurrentTime();
          } else {
            return Promise.reject('User doesn\'t have permission');
          }
        },
        (error) => Promise.reject('Couldn\'t get permissions =>', error));
    },
    (error) => Promise.reject('Couldn\'t get user'));
}
```


## Async-Await Syntactic Sugar
>The async and await keywords enable asynchronous, promise-based behavior to be written in a cleaner style, avoiding the need to explicitly configure promise chains.

_see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function_

Even though the readability is greatly improved it still doesn't read as smoothly as ordinary code. Async-Await was introduced as syntactic sugar that improves the readability of promises.

The example below expands on the getCurrentTimeForUser method and writes it in a cleaner way.

__Revisiting our Promise example__

```
function getUser() {
  return fetch('services/user').
    then((response) => response.json());
}

function getPermissions(userId) {
  return fetch('services/permissions/' + userId).
    then((response) => response.json());
}

function getCurrentTime() {
  return fetch('services/currentTime').
    then((response) => response.json());
}

// lets re-write this using promises
async function getTimeForUser() {
  try {
    const user = await getUser();
    const permissions = await getPermissions(user.userId);

    if (permissions.canDoThing) {
      const time = await getCurrentTime();
      return time;
    } else {
      return Promise.reject(new Error('Don\'t have permission to do thing'));
    }
  } catch (err) {
    console.error('Failed to get the time for the user');
  }
}
```

### Which to use? It depends
There are advantages and disadvantages of using the `Promise` style or the `Async-Await` style.

__Comparing Promises and Async-Await__
```
function getRandomNumber() {
  return fetch('/services/random').
    then(response => response.json());
}
// sometimes using async-await is more wordy
function getRandomNumberAlt() {
  const response = await fetch('/services/random');

  return response.json();
}

/**
 * Get numbers using promise style
 */
function getNumbersPromise() {

  const nums = [];
  for (let i = 0, end = 10; i < end; i++) {
    getRandomNumber().then((num) => {
      // to ensure proper ordering we
      // use closure
      // i think this should work?
      nums[i] = num;
    });
  }

}

/**
 * Get numbers using async-await
 * Using async await can result in more readable code
 */
function getNumbersAsync() {
  const nums = [];

  for (let i = 0, end = 10; i < end; i++) {
    // better this is more obvious
    // since we will wait to get the random
    // number before pushing it into the array
    const num = await getRandomNumber();
    nums.push(num);
  }
}

```

## A Brief overview RxJS Observables
Observables are the next iteration of Async Programming in JavaScript/TypeScript.

>A representation of any set of values over any amount of time.

_see https://rxjs.dev/api/index/class/Observable_

### Promises vs. Observables

A key distinction between `Promises` and `Observables` is that Observables can represent _0_, _1_ or _more_ emitted values.  This is similar to an event that can occur many times or not at all, like when the user clicks a button.


### Observables are Lazy
>Observables are lazy Push collections of multiple values.

_see https://rxjs.dev/guide/observable_

**What does it practically mean to be lazy?**

Why is it that nothing is executed when I call an observable function?  Newcomers to Observables often are confused by the _lazy_ behavior of Observables.

```
// a normal http request in angular (or whatever)
getThing(): any {
  return http.get('services/thing');
}

load(): void {
  // this will not invoke an ajax request
  // because we have not subscribed
  service.getThing();
}

loadWorking(): void {
  // this will invoke an ajax request
  // because we have subscribed for changes
  service.getThing().subscribe((data: any) => {
    this.doSomething(data);
  }
}

```

### Observables are like an event, and occur 0 or more times.

Observables are distinct from Promises in their ability to emit multiple values.

**Given the following observable**
```
const observable: Observable<any> = new Observable((subscriber: Subscriber) => {
  subscriber.next(0);
  subscriber.next(1);
  subscriber.next(2);
});
```
**Get only the first value**

I only care about the very first value emitted by an observable.
```
observable.pipe(first()).subscribe((value: any) => {
  console.log({ first: value });
});
// outputs 0
```

**Get only the last value**

I only care about the most recent value emitted by an observable.  _last_ May be a misnomer since it refers to:

>the last item from the source Observable that satisfies the predicate.
_see https://rxjs.dev/api/operators/last_
```
/**
 * I just want the last value emitted
 */
 observable.pipe(last()).subscribe((value: any) => {
  console.log({ last: value });
 });
 // outputs 2
```
**A note about toPromise**

The toPromise method returns the value of a completed observable.  This can confusing to some users who expect an observable to emit a value but it does not.

The code below will never execute the console.log statement because the observable never _completed_.
```
observable.toPromise().then((value: any) => {
  console.log({ prom: value });
});
// never outputs because toPromise only sends value when an observable is complete
```

_toPromise()_ will only resolve when an observable value has _completed_
```
cosnt completed: Observable<any> = new Observable((subscriber: Subscriber) => {
  subscriber.next(0);
  subscriber.next(1);
  subscriber.next(2);
  subscriber.complete();
});

completed.toPromise().then((value: any) => {
  console.log({ completed: value });
});
// outputs 2

```

### Mapping Operators
The various mapping operators allow a you to transform emitted values in an observable.

__map__

The base map operator allows you to transform an emitted value.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r4l6s7vs3qerjknaprpo.png)

```

getCurrentUser(): Observable<any> {
  return http.get('/services/currentUser');
}

// take the observable returned by getCurrentUser
getCurrentUserId(): Observable<string> {
  return getUser().pipe(
    // use map operator to map the user into some other value
    map((user: any) => user.userId));
}

```

__mergeMap/flatMap__

The _flatMap_ or _mergeMap_ operator is similar to the base `map` operator.  It takes a source observable's emitted values, and then merges them with another observable value to produce a resultant observable value.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cftny098cmo2j4bidxzi.png)

```

getPermissions(userId: string): Observable<Permissions> {
  return http.get(`/services/permissions/{$userId}`);
}

getCurrentUserPermissions(user: any): Observable<any> {
  // get the user observable
  const user: Observable<any> = getCurrentUser();

  return user.pipe(
    // use flatmap to combine results of the getPermissions()
    // observable and user observable
    flatMap((user: any) => getPermissions(user.userId)));
}

```

The key takeaway is that when you want to use another observable value, when mapping use the `flatMap` or `mergeMap` operator.

__concatMap__

_concatMap_ allows you to take a collection of values and emit them as distinct values.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/obzbi6qowm7idprc9g2c.png)

```
// ids => [1, 2, 3]
getUsers(ids: string[]): Observable<User[]> {
  return from(ids).pipe(
    // takes the list of ids and make multiple user calls
    concatMap((userId: string) => getUser(userId)))),
    // convert it to an array of users, we'll explain this later
    reduce((acc: User[], user: User) => acc.push(user), []);
}

```

__reduce__
_reduce_ is similar to the javascript `Array.reduce` method.  It consists of an accumlated value, and iterated value.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y78ox3amg8ks6pb1uvfa.png)

```
getUsers(ids: string[]): Observable<User[]> {
  return from(ids).pipe(
    // takes the list of ids and make multiple user calls
    concatMap((userId: string) => http.get(`services/user/${userId}`))),
    // convert it to an array of users, we'll explain this later
    reduce(
      // our accumulator function
      (acc: User[], user: User) => {
        acc.push(user);
        return acc;
      }, 
      // our starting value
      []);
}
```
There are many more mapping and other operators, we've only just touched a few of them.

### Functional Programming with observables
Using observable allows us to compose our observables from other observables

```
function getUser(): Observable<any> {
  return http.get('services/user');
}

function getPermissions(userId): Observable<any> {
  return http.get('services/permissions/' + userId);
}

function getCurrentTime(): Observable<number> {
  return http.get('services/currentTime');
}

getTimeForUser(): Observable<number> {
  return getUser().pipe(
    // after we get the user return the user id
    map((user: any) => user.userId),

    // pass the user id to another observable
    flatMap((userId: string) => getPermissions(userId)),

    // conditionally continue
    filter((permissions: any) => permissions.canDoThing),
    
    // get the current time
    flatMap(() => getCurrentTime()));
}
```
_see https://rxjs.dev/guide/operators for a complete list of operators_

_Composing Software: An Exploration of Functional Programming and Object Composition in JavaScript by Eric Elliot is a comprehensive overview of functional techniques_

### Observable Anti-Patterns

__Nested Subscriptions__
```
getTimeForUser(): any {
  // whats wrong here?
  return getUser().subscribe((user: any) => {
    getPermissions().subscribe((permissions: any) => {
      if (permissions.canDoThing) {
        return getCurrentTime();
      }
    })
  });
}

// what if any of these observables emit multiple values, or never complete?

// do you see the memory leak? 
```
__Converting to Promise__
```
// NO!
function getUserPromise(): Promise<any> {
  return new Promise((resolve) => {
    // do you see the memory leak?
    getUser().subscribe((user) => {
      resolve(user);
    });
  });
}

// Use built-in functions
function getUserPromiseWorking(): Promise<any> {
  // see caveat about using toPromise above
  return getUser().toPromise();
}
```

__Converting from Promise to Observable__
```
/**
 * Use the from operator to convert from promise to observable
 */
function fromUserPromise(): Observable<any> {
  return from(getUserPromise());
}

```

## Conclusion

Modern JavaScript and TypeScript provide powerful tools for asyncronous programming.  Promises and Async-Await are built in mechanisms that originated from the community and were so widely adopted they were introduced into the ECMA standards.  RxJS Observables are a powerful tool that build on Promises and Async-Await and extend them to create some useful mechanisms that are functionally oriented.