Three Ways to Handle Exceptions with Async Functions

Javascript exceptions shouldn’t be thrown when an IO  to error occurs. Instead, helpful messages should be sent your your logger system AND to the user.

Method 1: Try/Catch

Traditionally, these exceptions are handled using Try/Catch blocks, but those can easily make your code unreadable. For example,

const getCart = async id => ({items: [{productId: 1}]});
const getProduct = async id => ({imageId: 1});
const getImage = async id => {throw new Error("Image ID Not Found");}

const getCartFull = async (cartId) => {
  let cart, products = {}, images = {};
  
  try {
    cart = await getCart(cartId);
  
    for (let item of cart.items) {
      try {
        products[item.productId] = await getProduct(item.productId);

        try {
          let imageId = products[item.productId].imageId;
          images[imageId] = await getImage(imageId);
        } catch (error) {
          return [error];
        }

      } catch (error) {
        return [error];
      }
    }

  } catch (error) {
    return [error];
  }

  return [null, cart, products, images];
}

getCartFull(1).then(res => console.dir(res));

Method 2: Catch Callback

const getCart = async id => ({items: [{productId: 1}]});
const getProduct = async id => ({imageId: 1});
const getImage = async id => {throw new Error("Image ID Not Found");}

const getCartFull = async (cartId) => {
  let cart, products = {}, images = {}, error;

  cart = await getCart(cartId).catch(e => error = e);
  if(error) return [error];

  for (let item of cart.items) {
    products[item.productId] = await getProduct(item.productId).catch(e => error = e);
    if(error) return [error];

    let imageId = products[item.productId].imageId;
    images[imageId] = await getImage(imageId).catch(e => error = e)
    if(error) return [error];
  }

  return [null, cart, products, images];
}

getCartFull(1).then(res => console.dir(res));

Method 3: Custom Exception Wrapper

This method is inspired by Dima Grossman’s post.

const catcher = (promise) => {
  return promise.then(data => {
    return [null, data];
  })
  .catch(err => [err]);
}

const getCart = async id => ({items: [{productId: 1}]});
const getProduct = async id => ({imageId: 1});
const getImage = async id => {throw new Error("Image ID Not Found");}

const getCartFull = async (cartId) => {
  let cart, products = {}, images = {}, error;

  [error, cart] = await catcher(getCart(cartId));
  if(error) return [error];

  for (let item of cart.items) {
    [error, products[item.productId]] = await catcher(getProduct(item.productId));
    if(error) return [error];

    let imageId = products[item.productId].imageId;
    [error, images[imageId]] = await catcher(getImage(imageId));
    if(error) return [error];
  }

  return [null, cart, products, images];
}

getCartFull(1).then(res => console.dir(res));

Which to Choose?

Method 1 is just plain yuck. Method 2 is decent, I prefer Method 3 for readability.

What’s with Redux, ReduxSauce, Thunk, and Sagas?

What is Redux?

A library to create and manage data objects (aka state objects) based on the flux pattern.

Actions

Are requests to a store to take an action.

Example:
{ type: ADD_TODO, text: ‘Build my first Redux app’}

Action Types

An enum of allowable action types.

Reducers

Are handler functions for actions that apply the action payload.

myReducer = (state, action) => nextState

ReduxSauce

Is a library to make Redux more DRY. In doing so though, it makes the code less readable imo.

Thunk and Sagas

Calls to actions are synchronous. This means that action handlers cannot fetch data or have side effects. Thunk and Sagas solve this desire.

Reasons for: More powerful Redux can handle most of your biz logic
Reasons against: If not done carefully, you will end up with Redux spaghetti

Thunk

Thunk is an extended reducer which can (1) return a promise, and (2) actions are allowed to be called from within the promise (aka side-effects). For example, you can have an action which first sets a loading field = true in the store, fetches data, then updates the store when done.

Sagas

Sagas is like Thunk, but allows for action handling to be interrupted using generators

Further Reading

  • https://engineering.universe.com/what-is-redux-saga-c1252fc2f4d1

How to access object attributes based on a variable in Typescript

Typescript is obscurely particular with accessing attribute keys on objects that lack a generic signature. Adding generic signatures reduces type-safety though.

Here’s a Typescript-friendly way to verify an attribute exists in an object, and then access that attribute.

// implicitly typed object
const myObj = {
  Hello: "world"
};

const myObjKey = "Hello";

// The commonly recommended way to check if an attribute exists,  
// which throws a no signature lint error Element implicitly 
// has any type (TS7053) in Typescript
//
// if (myObj[myObjKey]) {
//     ...
// }

// The 'in' way, which Typescript likes
if (myObjKey in myObj) {
  // Now that we've confirmed the attribute exists, it's
  // type-safe to recast myObjKey and access it as an attribute
  console.log(myObj[myObjKey as keyof typeof myObj]);
}

This post originally posted at https://blog.smartlogic.io/accessing-object-attributes-based-on-a-variable-in-typescript/

How and when to use python’s lambda

There is a long running debate in python land of the usefulness of the lambda function, and I usually air on the side of avoidance.  However, for simple occasions it can make for cleaner code.

The python docs are essential for quick reference, but sometimes they skimp on obscure features.  Lambda is one of those features.

In a nutshell, lambda is the inline function creator for python. In other languages, inline functions often look like this:

myFunction = function(arg1,arg2) { return arg1+arg2 }

myFunction is a function, and myFunction(1,2)=3.  That makes a lot of sense! myFunction can similarly be defined in python using lambda:

myFunction = lambda arg1,arg2:  arg1+arg2

It’s true that this syntax requires less typing, but at the cost of readability.  In case you were wondering, here is a good example of when to use it.

Without lambda:

def isLessThanFour(x):
    return x<4
list_full = [1,2,3,4,5]
list_subset = filter(isLessThanFour, list_full)

With lambda:

list_full = [1,2,3,4,5]
list_subset = filter(lambda x: x<4, list_full)

Head caution though, because readability goes out the window whenever the lambda expression gets to complicated.

When not to use lambda:

reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])