ciorent 0.1.5 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +168 -156
- package/fiber.d.ts +9 -7
- package/fiber.js +1 -1
- package/index.d.ts +2 -2
- package/index.js +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
@@ -7,89 +7,103 @@ A lightweight, low-overhead concurrency library.
|
|
7
7
|
- Fully type-safe.
|
8
8
|
|
9
9
|
## Examples
|
10
|
-
###
|
11
|
-
|
12
|
-
|
10
|
+
### Utilities
|
11
|
+
#### Spawning tasks
|
12
|
+
Utilities to create and run tasks.
|
13
13
|
```ts
|
14
14
|
import * as co from 'ciorent';
|
15
|
-
import * as fiber from 'ciorent/fiber';
|
16
15
|
|
17
|
-
const
|
18
|
-
|
19
|
-
|
16
|
+
const task = async (id: number) => {
|
17
|
+
await co.sleep((10 - id) * 20 + 50);
|
18
|
+
console.log('Task', id, 'done');
|
19
|
+
}
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
// Spawn and run 5 tasks sequentially
|
22
|
+
console.log('Running 5 tasks sequentially:');
|
23
|
+
await co.sequential(5, task);
|
24
24
|
|
25
|
-
|
26
|
-
|
25
|
+
// Spawn and run 5 tasks concurrently
|
26
|
+
console.log('Running 5 tasks concurrently:');
|
27
|
+
await Promise.all(co.spawn(5, task));
|
28
|
+
```
|
27
29
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
console.log('Fiber 2 recieved:', res);
|
30
|
+
#### Sleep
|
31
|
+
Cross-runtime synchronous and asynchronous sleep functions.
|
32
|
+
```ts
|
33
|
+
import * as co from 'ciorent';
|
33
34
|
|
34
|
-
|
35
|
-
const childProc = fiber.spawn(f1);
|
36
|
-
fiber.mount(childProc, proc);
|
37
|
-
});
|
35
|
+
const logTime = (label: string) => console.log(label + ':', Math.floor(performance.now()) + 'ms');
|
38
36
|
|
39
|
-
|
37
|
+
logTime('Start');
|
40
38
|
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
// Non-blocking
|
40
|
+
await co.sleep(500);
|
41
|
+
logTime('After about 0.5s');
|
44
42
|
|
45
|
-
|
46
|
-
|
47
|
-
|
43
|
+
// This blocks the event loop
|
44
|
+
// On the browser this only works in workers and blocks the worker thread
|
45
|
+
co.sleepSync(500);
|
46
|
+
logTime('After another 0.5s');
|
47
|
+
```
|
48
48
|
|
49
|
-
|
50
|
-
|
49
|
+
#### Debounce
|
50
|
+
Postpones execution until after an idle period.
|
51
|
+
```ts
|
52
|
+
import * as co from 'ciorent';
|
51
53
|
|
52
|
-
|
53
|
-
console.log('
|
54
|
-
}
|
54
|
+
const fn = co.debounce((id: number) => {
|
55
|
+
console.log('ID:', id);
|
56
|
+
}, 500);
|
55
57
|
|
56
|
-
|
57
|
-
|
58
|
+
fn(1); // fn(1) gets skipped
|
59
|
+
await co.sleep(100);
|
60
|
+
fn(2); // fn(2) gets executed
|
61
|
+
```
|
58
62
|
|
59
|
-
|
60
|
-
|
63
|
+
#### Throttle
|
64
|
+
Executes a function at a regular interval.
|
65
|
+
```ts
|
66
|
+
import * as co from 'ciorent';
|
61
67
|
|
62
|
-
|
63
|
-
|
64
|
-
console.log('Fiber 1 stopped:', fiber.stopped(main));
|
65
|
-
}
|
66
|
-
```
|
68
|
+
// Allow 2 calls in 500ms
|
69
|
+
const throttle = co.throttle(500, 2);
|
67
70
|
|
68
|
-
|
69
|
-
|
71
|
+
co.spawn(8, async (id) => {
|
72
|
+
await throttle();
|
73
|
+
console.log(id + ': ' + Math.floor(performance.now()) + 'ms');
|
74
|
+
});
|
75
|
+
```
|
70
76
|
|
77
|
+
#### Pausing
|
78
|
+
Delay the execution of a function for other asynchronous tasks to run.
|
71
79
|
```ts
|
72
|
-
import * as
|
80
|
+
import * as co from 'ciorent';
|
73
81
|
|
74
|
-
|
82
|
+
// Expensive sync task
|
83
|
+
const task1 = async () => {
|
84
|
+
let x = 0;
|
75
85
|
|
76
|
-
|
77
|
-
//
|
78
|
-
await
|
86
|
+
// Yield control back to the runtime, allowing it to
|
87
|
+
// schedule other tasks
|
88
|
+
await co.pause;
|
79
89
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
}
|
90
|
+
// Simulate heavy operation
|
91
|
+
for (let i = 0; i < (Math.random() + 15) * 1e6; i++)
|
92
|
+
x += Math.random() * 32 + i * Math.round(Math.random() * 16);
|
84
93
|
|
85
|
-
|
86
|
-
|
87
|
-
console.log('Run before fetch:', performance.now().toFixed(2));
|
88
|
-
latch.open(startFetch);
|
89
|
-
}
|
94
|
+
console.log('Finish task 1:', x);
|
95
|
+
};
|
90
96
|
|
91
|
-
task
|
92
|
-
|
97
|
+
// Short async task
|
98
|
+
const task2 = async () => {
|
99
|
+
console.log('Start fetching...');
|
100
|
+
const txt = await fetch('http://example.com');
|
101
|
+
console.log('Fetch status', txt.status);
|
102
|
+
};
|
103
|
+
|
104
|
+
// Task 2 will not get blocked by task 1
|
105
|
+
task1();
|
106
|
+
task2();
|
93
107
|
```
|
94
108
|
|
95
109
|
### Channel
|
@@ -130,40 +144,31 @@ run();
|
|
130
144
|
console.log('Starting...');
|
131
145
|
```
|
132
146
|
|
133
|
-
###
|
134
|
-
|
147
|
+
### Latch
|
148
|
+
Latch is a synchronization primitive that allows one process to wait until another completes an operation before continuing execution.
|
135
149
|
|
136
150
|
```ts
|
137
|
-
import * as
|
138
|
-
import * as co from 'ciorent';
|
151
|
+
import * as latch from 'ciorent/latch';
|
139
152
|
|
140
|
-
const
|
153
|
+
const startFetch = latch.init();
|
141
154
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
await co.sleep(100);
|
146
|
-
topic.publish(messages, i);
|
147
|
-
}
|
155
|
+
const task = async () => {
|
156
|
+
// Blocks until the latch is open
|
157
|
+
await latch.pause(startFetch);
|
148
158
|
|
149
|
-
|
150
|
-
|
151
|
-
|
159
|
+
console.log('Start fetching...');
|
160
|
+
const res = await fetch('http://example.com');
|
161
|
+
console.log('Fetch status:', res.status);
|
152
162
|
}
|
153
163
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
// Block until the value is sent
|
160
|
-
const x = await topic.recieve(sub);
|
161
|
-
if (x == null) break;
|
162
|
-
console.log(`Task ${id} recieved: ${x}`);
|
163
|
-
}
|
164
|
-
});
|
164
|
+
const prepare = () => {
|
165
|
+
// This always run first
|
166
|
+
console.log('Run before fetch:', performance.now().toFixed(2));
|
167
|
+
latch.open(startFetch);
|
168
|
+
}
|
165
169
|
|
166
|
-
|
170
|
+
task();
|
171
|
+
prepare();
|
167
172
|
```
|
168
173
|
|
169
174
|
### Semaphore
|
@@ -195,101 +200,108 @@ const task = async (id: number) => {
|
|
195
200
|
co.spawn(5, task);
|
196
201
|
```
|
197
202
|
|
198
|
-
###
|
199
|
-
|
200
|
-
|
203
|
+
### Pubsub
|
204
|
+
A fast, simple publish-subscribe API.
|
205
|
+
|
201
206
|
```ts
|
207
|
+
import * as topic from 'ciorent/topic';
|
202
208
|
import * as co from 'ciorent';
|
203
209
|
|
204
|
-
|
205
|
-
const task1 = async () => {
|
206
|
-
let x = 0;
|
210
|
+
const messages = topic.init<number>();
|
207
211
|
|
208
|
-
|
209
|
-
|
210
|
-
|
212
|
+
// A task that publish messages
|
213
|
+
const publisher = async () => {
|
214
|
+
for (let i = 0; i < 3; i++) {
|
215
|
+
await co.sleep(100);
|
216
|
+
topic.publish(messages, i);
|
217
|
+
}
|
211
218
|
|
212
|
-
//
|
213
|
-
|
214
|
-
|
219
|
+
// Resolve all waiting promises
|
220
|
+
// And clear the value queue
|
221
|
+
topic.flush(messages);
|
222
|
+
}
|
215
223
|
|
216
|
-
|
217
|
-
|
224
|
+
// Spawn 3 tasks that recieve messages
|
225
|
+
co.spawn(3, async (id: number) => {
|
226
|
+
const sub = topic.subscribe(messages);
|
218
227
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
}
|
228
|
+
while (true) {
|
229
|
+
// Block until the value is sent
|
230
|
+
const x = await topic.recieve(sub);
|
231
|
+
if (x == null) break;
|
232
|
+
console.log(`Task ${id} recieved: ${x}`);
|
233
|
+
}
|
234
|
+
});
|
225
235
|
|
226
|
-
|
227
|
-
task1();
|
228
|
-
task2();
|
236
|
+
publisher();
|
229
237
|
```
|
230
238
|
|
231
|
-
|
232
|
-
|
239
|
+
### Fibers
|
240
|
+
Virtual threads with more controlled execution.
|
241
|
+
|
233
242
|
```ts
|
234
243
|
import * as co from 'ciorent';
|
244
|
+
import * as fiber from 'ciorent/fiber';
|
235
245
|
|
236
|
-
const
|
246
|
+
const f1 = fiber.fn(function* () {
|
247
|
+
// Wait for a promise
|
248
|
+
yield co.sleep(1000);
|
237
249
|
|
238
|
-
|
250
|
+
// Wait for a promise and return its result
|
251
|
+
const res = yield* fiber.unwrap(Promise.resolve(1));
|
252
|
+
console.log('Fiber 1 recieved:', res);
|
239
253
|
|
240
|
-
|
241
|
-
|
242
|
-
logTime('After about 0.5s');
|
254
|
+
return Math.random();
|
255
|
+
});
|
243
256
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
257
|
+
{
|
258
|
+
const main = fiber.spawn(function* (proc) {
|
259
|
+
// Start f1, wait for the process to complete and get the result
|
260
|
+
const res = yield* fiber.join(fiber.spawn(f1));
|
261
|
+
console.log('Fiber 2 recieved:', res);
|
249
262
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
263
|
+
// Start f1 and make its lifetime depends on current fiber
|
264
|
+
const childProc = fiber.spawn(f1);
|
265
|
+
fiber.mount(childProc, proc);
|
266
|
+
});
|
254
267
|
|
255
|
-
|
256
|
-
await co.sleep((10 - id) * 20 + 50);
|
257
|
-
console.log('Task', id, 'done');
|
258
|
-
}
|
268
|
+
console.log('Fiber 2 started:', fiber.resumed(main));
|
259
269
|
|
260
|
-
//
|
261
|
-
|
262
|
-
|
270
|
+
// Pause the current fiber process
|
271
|
+
fiber.pause(main);
|
272
|
+
console.log('Fiber 2 is paused:', fiber.paused(main));
|
263
273
|
|
264
|
-
//
|
265
|
-
|
266
|
-
|
267
|
-
```
|
274
|
+
// Resume the fiber
|
275
|
+
fiber.resume(main);
|
276
|
+
console.log('Fiber 2 is resumed:', fiber.resumed(main));
|
268
277
|
|
269
|
-
|
270
|
-
|
271
|
-
```ts
|
272
|
-
import * as co from 'ciorent';
|
278
|
+
// Wait for the fiber process to finish
|
279
|
+
await fiber.done(main);
|
273
280
|
|
274
|
-
|
275
|
-
console.log('
|
276
|
-
}
|
281
|
+
// Check finish status
|
282
|
+
console.log('Fiber 2 completed:', fiber.completed(main));
|
283
|
+
}
|
277
284
|
|
278
|
-
|
279
|
-
|
280
|
-
fn(2); // fn(2) gets executed
|
281
|
-
```
|
285
|
+
{
|
286
|
+
console.log('------------------------');
|
282
287
|
|
283
|
-
|
284
|
-
|
285
|
-
```ts
|
286
|
-
import * as co from 'ciorent';
|
288
|
+
const main = fiber.spawn(f1);
|
289
|
+
console.log('Fiber 1 started:', fiber.resumed(main));
|
287
290
|
|
288
|
-
//
|
289
|
-
|
290
|
-
console.log(
|
291
|
-
}
|
291
|
+
// Stop a fiber
|
292
|
+
fiber.interrupt(main);
|
293
|
+
console.log('Fiber 1 interrupted:', fiber.interrupted(main));
|
294
|
+
}
|
295
|
+
|
296
|
+
{
|
297
|
+
console.log('------------------------');
|
292
298
|
|
293
|
-
|
299
|
+
const main = fiber.spawn(f1);
|
300
|
+
console.log('Fiber 1 started:', fiber.resumed(main));
|
301
|
+
|
302
|
+
// Timeout a fiber
|
303
|
+
await fiber.timeout(main, 500);
|
304
|
+
console.log('Fiber 1 stopped:', fiber.interrupted(main));
|
305
|
+
}
|
294
306
|
```
|
295
307
|
|
package/fiber.d.ts
CHANGED
@@ -41,11 +41,7 @@ export declare const completed: (t: Process) => boolean;
|
|
41
41
|
/**
|
42
42
|
* Check whether the fiber has been interrupted
|
43
43
|
*/
|
44
|
-
export declare const
|
45
|
-
/**
|
46
|
-
* Create a fiber function
|
47
|
-
* @param f
|
48
|
-
*/
|
44
|
+
export declare const interrupted: (t: Process) => boolean;
|
49
45
|
export declare const fn: <const Fn extends (thread: Process, ...args: any[]) => Generator>(f: Fn) => Fn;
|
50
46
|
/**
|
51
47
|
* A basic fiber runtime
|
@@ -63,10 +59,16 @@ export declare const pause: (t: Process) => void;
|
|
63
59
|
*/
|
64
60
|
export declare const resume: (t: Process) => void;
|
65
61
|
/**
|
66
|
-
*
|
62
|
+
* Interrupt the execution of a fiber
|
63
|
+
* @param t
|
64
|
+
*/
|
65
|
+
export declare const interrupt: (t: Process) => void;
|
66
|
+
/**
|
67
|
+
* Timeout a fiber
|
67
68
|
* @param t
|
69
|
+
* @param ms
|
68
70
|
*/
|
69
|
-
export declare const
|
71
|
+
export declare const timeout: (t: Process, ms: number) => Promise<void>;
|
70
72
|
/**
|
71
73
|
* Wait for a fiber and retrieve its result
|
72
74
|
* @param t
|
package/fiber.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export let paused=(t)=>t[1]===0;export let resumed=(t)=>t[1]===1;export let completed=(t)=>t[1]===2;export let
|
1
|
+
import{sleep}from"./index.js";export let paused=(t)=>t[1]===0;export let resumed=(t)=>t[1]===1;export let completed=(t)=>t[1]===2;export let interrupted=(t)=>t[1]===3;let invoke=async(g,thread)=>{try{let t=g.next();while(!t.done){let v=await t.value;if(thread[1]===0){let r;let p=new Promise((res)=>{r=res});thread[2]=r;await p}if(thread[1]===3)return;t=g.next(v)}thread[1]=2;return t.value}finally{if(thread[1]!==2)thread[1]=3;thread[3].forEach(stop)}};export let fn=(f)=>f;export let spawn=(f,...args)=>{let thread=[null,1,null,[]];thread[0]=invoke(f(thread,...args),thread);return thread};export let pause=(t)=>{if(t[1]===1)t[1]=0};export let resume=(t)=>{if(t[1]===0){t[1]=1;t[2]?.()}};export let interrupt=(t)=>{if(t[1]!==2){if(t[1]===0)t[2]?.();t[1]=3}};export let timeout=async(t,ms)=>{await sleep(ms);interrupt(t)};export function*join(t){return yield t[0]}export let done=(t)=>t[0];export let mount=(child,parent)=>{parent[3].push(child)};export let control=(t,signal)=>{signal.addEventListener("abort",()=>{interrupt(t)})};export function*unwrap(t){return yield t}
|
package/index.d.ts
CHANGED
@@ -24,7 +24,7 @@ export declare const sleep: (ms: number) => Promise<void>;
|
|
24
24
|
*/
|
25
25
|
export declare const sleepSync: (ms: number) => void;
|
26
26
|
/**
|
27
|
-
* Spawn n
|
27
|
+
* Spawn n sequential task
|
28
28
|
* @param n
|
29
29
|
* @param task - The function to run
|
30
30
|
*/
|
@@ -47,4 +47,4 @@ export declare const debounce: <const Args extends any[]>(f: (...args: Args) =>
|
|
47
47
|
* @param ms - The time in milliseconds
|
48
48
|
* @param limit - The call limit in the time period
|
49
49
|
*/
|
50
|
-
export declare const throttle:
|
50
|
+
export declare const throttle: (ms: number, limit: number) => (() => Promise<void>);
|
package/index.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export let pause=Promise.resolve();export let sleep=globalThis.Bun?.sleep??globalThis.process?.getBuiltinModule?.("timers/promises").setTimeout??((ms)=>new Promise((res)=>{setTimeout(res,ms)}));let sharedBuf=new Int32Array(new SharedArrayBuffer(4));export let sleepSync=globalThis.Bun?.sleepSync??((ms)=>{Atomics.wait(sharedBuf,0,0,ms)});export let sequential=async(n,task,...args)=>{for(let i=0;i<n;i++)await task(...args,i)};export let spawn=(n,task,...args)=>{let arr=new Array(n);for(let i=0;i<n;i++)arr[i]=task(...args,i);return arr};export let debounce=(f,ms)=>{let id;return(...a)=>{clearTimeout(id);id=setTimeout(f,ms,...a)}};export let throttle=(
|
1
|
+
export let pause=Promise.resolve();export let sleep=globalThis.Bun?.sleep??globalThis.process?.getBuiltinModule?.("timers/promises").setTimeout??((ms)=>new Promise((res)=>{setTimeout(res,ms)}));let sharedBuf=new Int32Array(new SharedArrayBuffer(4));export let sleepSync=globalThis.Bun?.sleepSync??((ms)=>{Atomics.wait(sharedBuf,0,0,ms)});export let sequential=async(n,task,...args)=>{for(let i=0;i<n;i++)await task(...args,i)};export let spawn=(n,task,...args)=>{let arr=new Array(n);for(let i=0;i<n;i++)arr[i]=task(...args,i);return arr};export let debounce=(f,ms)=>{let id;return(...a)=>{clearTimeout(id);id=setTimeout(f,ms,...a)}};export let throttle=(ms,limit)=>{let head=[null];let tail=head;let cur=limit;let scheduled=false;let unlock=()=>{cur=limit;if(tail===head){scheduled=false;return}do{cur--;(tail=tail[0])[1]()}while(cur>0&&tail!==head);setTimeout(unlock,ms)};return()=>{if(cur===0){let r;let p=new Promise((res)=>{r=res});head=head[0]=[null,r];return p}if(!scheduled){scheduled=true;setTimeout(unlock,ms)}cur--;return pause}};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "ciorent",
|
3
|
-
"version": "0.1
|
3
|
+
"version": "0.2.1",
|
4
4
|
"description": "A lightweight, low-overhead concurrency library",
|
5
5
|
"homepage": "https://ciorent.netlify.app",
|
6
6
|
"repository": {
|
@@ -18,14 +18,14 @@
|
|
18
18
|
"main": "./index.js",
|
19
19
|
"types": "./index.d.ts",
|
20
20
|
"exports": {
|
21
|
-
"./fixed-queue": "./fixed-queue.js",
|
22
21
|
"./sliding-queue": "./sliding-queue.js",
|
23
|
-
"./
|
24
|
-
"./channel": "./channel.js",
|
22
|
+
"./fixed-queue": "./fixed-queue.js",
|
25
23
|
"./fiber": "./fiber.js",
|
26
|
-
"./dropping-queue": "./dropping-queue.js",
|
27
24
|
".": "./index.js",
|
25
|
+
"./dropping-queue": "./dropping-queue.js",
|
26
|
+
"./channel": "./channel.js",
|
28
27
|
"./topic": "./topic.js",
|
29
|
-
"./semaphore": "./semaphore.js"
|
28
|
+
"./semaphore": "./semaphore.js",
|
29
|
+
"./latch": "./latch.js"
|
30
30
|
}
|
31
31
|
}
|