It is the beauty of Python future that the process forking and merging is maintained at the syntactic level. Future producer determines when to fork from the main control flow, however, the main flow dictates exactly when to merge. Such a separation of concerns enables us to express complicated business logic in a terse and concise way. Consider the following ticket booking requirement:

Ticket Booking Flow:

  1. User must be authenticated
  2. If user has booked for “Star Wars: the Force Awakens”, give him a 50% discount over the subtotal
  3. If user is the first to book, give him an early bird promo to be applied immediately.
  4. Return the due total

The corresponding Python code should look as follows:

def get_due_total():
    if not user.authenticated:
        raise Exception('User is not authenticated')
    is_discount_50 = user_has_booked('starwars-the-force-awakens') # fork (a)
    tickets_already_booked = count_tickets(movie_id) # fork (b)
    subtotal_result = yield get_subtotal(ticket_ids) # fork and merge immediately
    if total_result == 0:
        return dict(due=0)
    else:
        returned_value = dict()
        is_discount_50_result, tickets_already_booked_result = yield is_discount_50, tickets_already_booked # merge (a) and (b)
        if is_discount_50_result:
            returned_value['due'] = total_result / 2
        if tickets_already_booked_result < 10:
            returned_value['promo'] = 'early-bird'
        return returned_value

People would not even notice the asynchronous nature of the code fragment above! Let’s do the same thing with NodeJS.

function getDueTotal() {
    if (!user.authenticated) {
      throw new Error('User is not authenticated');
    }
    isDiscount50 = userHasBooked('starwar');
    ticketsAlreadyBooked = countTickets(movieId);
    return getSubtotal(ticketIds).then((subtotal) => {
      if (subtotal === 0) {
        return {due: 0};
      }
      return Promise.all([isDiscount50, ticketsAlreadyBooked]).then(([isDiscount50Result, ticketAlreadyBookedResult]) => {
        var returnedValue = {};
        if (isDiscount50Result)
          returnedValue['due'] = subtotal/2;
        if (ticketAlreadyBookedResult < 10)
          returnedValue['promo'] = 'early-bird';
        return returnedValue;
      });
    });
}

I have left out all the error handling code in both cases to keep my point clear. The number of lines are nearly the same in both cases. However line count is trivial compared to the possibility of changes. What if we need to add more business rules, or re-order the steps to execute the existing rules? Python’s top down linear execution has the following implications:

  • Easier to read and understand
  • Harder to make mistakes and easier to debug
  • Smaller signal to noise ratio (requirements vs language specific syntax) Personally it would be more pleasant to make changes which reflect the new requirements in Python than NodeJS. How about you?