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 tag
s 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()
…
-
flatten(), as a
sender
to event, -
walk(), as a
receiver
from event, -
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)
})
}
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'
]
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))
})
}
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 array
s:
$ 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'
]
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 ].