cli  
Article Series

This article series discuss more than 18 different programming languages. Please read overview before you read any of the details.

Playing with Records Related Articles.

Table of Content
Where to Discuss?

Local Group

Preface

Goal: A simple case example of concurrency with event emitter in nodejs.

We can pass data between two different functions that run separately. Apart from previously functional topic with javascript, we can build a simple concurreny example based on previous data.


6: Concurrency with Event Emitter

Concurrency in nodejs, is surprisingly easy. We have a show case to pass data, between two functions that run separately.

We are going to use sender/receiver actor model. In other reference this is also referred as producer/customer. Instead of channel like goroutine, javascript utilize event emitter. Whatever the name is, now we know that it is also works with nodejs.

Reference

Event Emitter in Browser

This case example works in nodejs backend only. For browser, you might consider Event Target.

Custom Flatten Function

In order to make a Sender demo, I need to make custom handmade flatten function.

import songs from "./songs-data.js";

const flatten = songs => {
  let tags = []

  songs.forEach(song => {
    if( "tags" in song ) {
      (song.tags).forEach(tag => {
        tags.push(tag)
      })
    }
  })

  return tags
}

console.log(flatten(songs))

With the result similar as below array:

$ node 32-songs-flatten.js
[
  '60s', 'jazz',
  '60s', 'rock',
  '70s', 'rock',
  '70s', 'pop'
]

This is simply flatten using all tags from all songs records, using nested for loop.

This way we can send the result, using event emitter in sender functions, instead of directly pushing the result to array.

The Skeleton

We should be ready for the real demo. This is only consist of one short file.

import songs from "./songs-data.js"
import { EventEmitter } from"events"

// Receiver/Customer
const walk = (eventEmitter) => {}

// Sender/Producer
const flatten = (eventEmitter) => {}

// Program Entry Point
const eventEmitter = new EventEmitter()

  1. flatten(), as a sender to event,

  2. walk(), as a receiver from event,

  3. Program entry point

Sender

The flatten function act as sender, that push each new tag to event.

const flatten = (eventEmitter) => {
  songs.forEach(song => {
    if( "tags" in song ) {
      (song.tags).forEach(tag => {
        eventEmitter.emit('tag', tag)
      })
    }
  })
}

We would gather all event separately to form a new tags list.

Receiver

The walk function act as receiver.

  const walk = (eventEmitter) => {
  let tags = []

  eventEmitter.on('tag', tag => {
    tags.push(tag)
  })
  eventEmitter.on('quit', () => {
    console.log(tags)
  })
}

NodeJS: Concurreny with Event Emitter: Sender and Receiver

Running Both Separately

Pretty short right!

Consider gather both functions in program entry point.

const eventEmitter = new EventEmitter()

walk(eventEmitter)
flatten(eventEmitter)
eventEmitter.emit('quit')

With the result as below array:

$ node 33-songs-event.js
[
  '60s', 'jazz',
  '60s', 'rock',
  '70s', 'rock',
  '70s', 'pop'
]

NodeJS: Concurreny with Event Emitter: Sender and Receiver

This is the end of our Ecmascript journey in this article. We shall meet again in other article.

7: Concurrency with Promise

Not the right pattern 🤔?

I have been asking myself, if I can have alternative pattern, and get rid of event emitter.

Promise

The short answer is, I can’t.

Promise use case is made for chained asynchronous. I can utilize promise with receiver inside sender, but I still have to use global tags variable.

import songs from "./songs-data.js"
let tags = []

Receiver

However this is what I found out, just in case someone need to experiment with it.

// Receiver/Customer
const take = (message) => {
return new Promise(resolve => { 
  setTimeout(() => {
    if( "tag"  in message ) {
      tags.push(message.tag);
      resolve(message.tag)
    }
  }, Math.floor(Math.random() * 1000))
})
}

NodeJS: Concurreny with Promise: Receiver

We are using timeout to simulate data fetching.

Sender

// Sender/Producer
const flatten = () => {
  let promises = []

  songs.forEach(song => {
    if( "tags" in song ) {
      (song.tags).forEach(tag => {
        promises.push(take({ tag: tag }))
      })
    }
  })
  
  return Promise.all(promises)
}

Compare The Result

And finally we can compare the ordered result, with random timeout result:

flatten().then(values => {
    console.log('Resolve Values: ', values);
    console.log('Global Tags: ', tags);
  });

With the result as below two different arrays:

$ node 34-songs-async.js
Resolve Values:  [
  '60s', 'jazz',
  '60s', 'rock',
  '70s', 'rock',
  '70s', 'pop'
]
Global Tags:  [
  'jazz', '70s',
  'rock', '70s',
  '60s',  'pop',
  'rock', '60s'
]

NodeJS: Concurreny with Promise: Sender

I know it won’t be like the previous event emitter use case.

Conclusion

Promise has different use case.


What is Next 🤔?

The rising programming language, the typescript.

Consider continue reading [ Typescript - Small Step into Typed - Part One ].