1+ import express , { Express , Request , Response } from 'express' ;
2+ import { Worker } from 'worker_threads' ;
3+ import dotenv from 'dotenv' ;
4+ import { cpuIntensiveFunc } from './cpu_intensive_func' ;
5+ import { ioIntensiveFunc } from './io_intensive_func' ;
6+ import { queue } from './queue' ;
7+ import { tasks } from './tasks' ;
8+
9+ dotenv . config ( ) ;
10+ const app : Express = express ( ) ;
11+ const port = process . env . PORT ;
12+
13+ // experiment 1:
14+ // first hit: http://localhost:8000/cpu-intensive-task
15+ // then hit: http://localhost:8000/quick-task
16+ // observe that the server can't process the quick task until it finishes processing the cpu intensive task first
17+ // it's because the cpu intensive task blocks the event loop (all the requests are handled in the main thread, same as the event loop itself)
18+ app . get ( '/cpu-intensive-task' , ( req : Request , res : Response ) => {
19+ console . log ( 'received cpu intensive request' ) ;
20+ cpuIntensiveFunc ( ) ;
21+ const msg = 'finished cpu intensive request' ;
22+ res . send ( msg ) ;
23+ console . log ( msg ) ;
24+ } ) ;
25+
26+ // experiment 2:
27+ // first hit: http://localhost:8000/cpu-intensive-task
28+ // then hit: http://localhost:8000/quick-task
29+ // observe that the server can continue to process the quick task while the cpu intensive task is handled in the worker thread.
30+ // it's because we off load the cpu intensive task to a worker thread, therefore it doesn't block the event loop anymore.
31+ app . get ( '/cpu-intensive-task-off-loaded-to-worker' , ( req : Request , res : Response ) => {
32+ console . log ( 'received cpu intensive request and off load to worker thread' ) ;
33+ const worker = new Worker ( './dist/worker.js' ) ;
34+ worker . on ( 'message' , ( ) => {
35+ const msg = 'finished cpu intensive request in worker thread' ;
36+ res . send ( msg ) ;
37+ console . log ( msg ) ;
38+ } ) ;
39+ } ) ;
40+
41+ // experiment 3:
42+ // first hit: http://localhost:8000/io-intensive-task
43+ // then hit: http://localhost:8000/quick-task
44+ // observe that the server can continue to process the quick task while waiting for the io intensive task to finish.
45+ // it's because the io intensive task yields the call stack while waiting on the io to finish, so event loop can continue server the next received request.
46+ app . get ( '/io-intensive-task' , ( req : Request , res : Response ) => {
47+ console . log ( 'received io intensive request' ) ;
48+ ioIntensiveFunc ( ( ) => {
49+ const msg = 'finished io intensive request' ;
50+ res . send ( msg ) ;
51+ console . log ( msg ) ;
52+ } ) ;
53+ } ) ;
54+ app . get ( '/quick-task' , ( req : Request , res : Response ) => {
55+ console . log ( 'received quick request' ) ;
56+ const msg = 'finished quick request' ;
57+ res . send ( msg ) ;
58+ console . log ( msg ) ;
59+ } ) ;
60+
61+ // experiment 4:
62+ // set up:
63+ // we have a queue whose concurrent value is set to 1, it means only 1 task can be handled at a time.
64+ //
65+ // task 1: cpu intensive
66+ // task 2: quick task
67+ // task 3: io intensive
68+ //
69+ // first hit: http://localhost:8000/sequential-task/1
70+ // then hit: sequential-task/2
71+ // observe that the server can't process task 2 before task 1 is done. It actually can't even be enqueued.
72+
73+ // experiment 5:
74+ // first hit: http://localhost:8000/sequential-task/3
75+ // then hit: sequential-task/2
76+ // observe that the task 2 can be enqueued while server is processing task 3. But server can't switch to process task 2
77+ // even though task 3 is io bond.
78+ //
79+ // If we don't have the queue, task 2 can actually be run immediately and finish before task 3. THe purpose of
80+ // the queue is basically to ensure the order of the tasks execution, which can be important in situations where
81+ // data consistency etc is needed.
82+ //
83+ // Just to call out that, we can actually call the done method early to unblock the queue early on.
84+ // See comment in queue.ts
85+ app . get ( '/sequential-task/:id' , ( req : Request , res : Response ) => {
86+ const taskId = parseInt ( req . params . id ) ;
87+ const task = tasks . find ( task => task . id === taskId ) ;
88+ if ( ! task ) {
89+ res . send ( 'task not found' ) ;
90+ return ;
91+ }
92+ console . log ( `received task ${ taskId } ` ) ;
93+ queue . push ( task , ( ) => {
94+ const msg = `finished processing task ${ taskId } ` ;
95+ res . send ( msg ) ;
96+ console . log ( msg ) ;
97+ } )
98+ } ) ;
99+
100+ app . listen ( port , ( ) => {
101+ console . log ( `⚡️[server]: Server is running at http://localhost:${ port } ` ) ;
102+ } ) ;
0 commit comments