Skip to content

Commit 9a29886

Browse files
Chang LiuChang Liu
authored andcommitted
init
0 parents  commit 9a29886

File tree

11 files changed

+2810
-0
lines changed

11 files changed

+2810
-0
lines changed

.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
PORT=8000

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/node_modules
2+
/dist

cpu_intensive_func.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const isPrime = (num: number) => {
2+
for (let i = 2, s = Math.sqrt(num); i <= s; i++) {
3+
if (num % i === 0) return false;
4+
}
5+
return num > 1;
6+
}
7+
export function cpuIntensiveFunc() {
8+
console.log('Begin cpu intensive task: find prime number');
9+
const primeNumbers = [];
10+
for (let i = 0; i < 0.3e8; i++) {
11+
if (isPrime(i)) {
12+
primeNumbers.push(i);
13+
}
14+
}
15+
console.log(`Finished cpu intensive task: ${primeNumbers.length} prime numbers found`);
16+
}

index.ts

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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+
});

io_intensive_func.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function ioIntensiveFunc(cb: () => void) {
2+
// simulate some io intensive task with setTimeout
3+
setTimeout(() => {
4+
cb();
5+
}, 5000);
6+
}
7+

0 commit comments

Comments
 (0)