├── .gitignore
├── src
├── index.js
├── AISource.js
├── AIWorker.js
└── AsyncQueue.js
├── .editorconfig
├── rollup.config.js
├── package.json
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist/
3 | coverage/
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export { AISource } from './AISource';
2 | export { AIWorker } from './AIWorker';
3 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 |
10 | # editorconfig-tools is unable to ignore longs strings or urls
11 | max_line_length = 120
--------------------------------------------------------------------------------
/src/AISource.js:
--------------------------------------------------------------------------------
1 | import { AsyncQueue } from "./AsyncQueue";
2 |
3 | export class AISource {
4 | constructor() {
5 |
6 | this.messages = new AsyncQueue();
7 |
8 | self.onmessage = (message) => this.messages.put(message);
9 |
10 | }
11 |
12 | async postMessage(message, transfer) {
13 | self.postMessage(message, transfer)
14 | return this.messages.get();
15 | }
16 |
17 | reply(message, transfer) {
18 | self.postMessage(message, transfer);
19 | }
20 |
21 | async *[Symbol.asyncIterator]() {
22 | while(true) {
23 | yield this.messages.get();
24 | }
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import cleanup from "rollup-plugin-cleanup";
2 | import minify from "rollup-plugin-babel-minify";
3 | import pkg from "./package.json";
4 |
5 | export default {
6 | input: "src/index.js",
7 | output: [
8 | {
9 | file: pkg.main,
10 | format: "umd",
11 | sourcemap: true,
12 | name: "aww"
13 | }
14 | ],
15 | external: [
16 | ...Object.keys(pkg.dependencies || {}),
17 | ...Object.keys(pkg.devDependencies || {})
18 | ],
19 | plugins: [
20 | cleanup({
21 | comments: "none",
22 | extensions: ["js"]
23 | }),
24 | minify({}),
25 | ]
26 | };
27 |
--------------------------------------------------------------------------------
/src/AIWorker.js:
--------------------------------------------------------------------------------
1 | import { AsyncQueue } from "./AsyncQueue";
2 |
3 |
4 | export class AIWorker {
5 | constructor(worker) {
6 | this.worker = worker;
7 | this.messages = new AsyncQueue();
8 |
9 | this.worker.onmessage = (message) => this.messages.put(message);
10 |
11 | }
12 |
13 |
14 | async postMessage(message, transfer) {
15 | this.worker.postMessage(message, transfer);
16 | return this.messages.get();
17 | }
18 |
19 | reply(message, transfer) {
20 | this.worker.postMessage(message, transfer);
21 | }
22 |
23 | async *[Symbol.asyncIterator]() {
24 | while(true) {
25 | yield this.messages.get();
26 | }
27 | }
28 |
29 | terminate() {
30 | this.worker.terminate();
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aww",
3 | "version": "0.1.0",
4 | "description": "Async Iterables Interfaces for Web Workers",
5 | "main": "dist/index.js",
6 | "files": [
7 | "/dist"
8 | ],
9 | "scripts": {
10 | "build": "rollup -c"
11 | },
12 | "keywords": [
13 | "web",
14 | "workers",
15 | "async",
16 | "iterables"
17 | ],
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/jfet97/aww.git"
21 | },
22 | "author": "Andrea Simone Costa andrysimo1997@gmail.com",
23 | "license": "MIT",
24 | "engines": {
25 | "node": ">=10.0.0"
26 | },
27 | "devDependencies": {
28 | "rollup": "^1.20.3",
29 | "rollup-plugin-babel-minify": "^9.0.0",
30 | "rollup-plugin-cleanup": "^3.1.1"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/AsyncQueue.js:
--------------------------------------------------------------------------------
1 | export class AsyncQueue {
2 | constructor(...values) {
3 |
4 | this.promise = new Promise(resolve => {
5 | this.resolve = resolve;
6 | });
7 |
8 | values.forEach(v => this.put(v));
9 | }
10 |
11 | put(value) {
12 | // advance the tail
13 |
14 | let resolveNext = null;
15 | const nextPromise = new Promise(resolve => {
16 | resolveNext = resolve;
17 | });
18 |
19 | this.resolve({
20 | value: Promise.resolve(value), // normalize the value
21 | nextPromise,
22 | });
23 |
24 | this.resolve = resolveNext;
25 | }
26 |
27 | get() {
28 | // advance the head
29 |
30 | const resultingPromise = this.promise.then(({ value }) => value);
31 | const actualPromise = this.promise;
32 |
33 | // defer next node resolution until the current is solved
34 | this.promise = resultingPromise.then(() => actualPromise).then(({ nextPromise }) => nextPromise);
35 |
36 | return resultingPromise;
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Andrea Simone Costa
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # aww
2 |
3 | __Async Iterables Interfaces for Web Workers__
4 |
5 | This is a proof of concept library that enables async iteration with web workers. Errors handling is not yet supported.
6 |
7 | ```sh
8 | npm i -S aww
9 | ```
10 |
11 | Do not use the `postMessage` method of my wrappers while you are async iterating: use the `reply` method instead.
12 |
13 |
14 | ## How to iterate over the invoker's incoming data (from the worker perspective)
15 |
16 | ### worker.js
17 | ```js
18 | import { AISource } from 'aww';
19 |
20 | const delay = (ms, v) => new Promise(res => setTimeout(res, ms, v));
21 |
22 | ;(async () => {
23 |
24 | // will handle onmessage events and other stuff
25 | // it's an async iterable
26 | const source = new AISource();
27 |
28 | for await(const msg of source) {
29 | console.log(`${msg.data} received inside worker`);
30 | await delay(2000, msg); // fake long task
31 |
32 | // next messages from the main will be handled by the async iteration
33 | // no sense making this awaitable
34 | // DO NOT USE postMessage!
35 | source.reply("It's all good from worker!");
36 | }
37 |
38 | })();
39 | ```
40 |
41 | ### main.js
42 | ```js
43 | import { AIWorker } from 'aww';
44 |
45 | const delay = (ms, v) => new Promise(res => setTimeout(res, ms, v));
46 |
47 | ;(async () => {
48 |
49 | const myWorker = new AIWorker(new Worker('./worker.js'));
50 |
51 | let i = 0;
52 | while(true) {
53 | await delay(1000);
54 |
55 | // you can await the response from the worker, but it is not mandatory
56 | await myWorker.postMessage(i++).then(({data}) => console.log(data));
57 |
58 | /*
59 | alternative syntax
60 | const { data } = await myWorker.postMessage(i++);
61 | console.log(data);
62 | */
63 | }
64 |
65 | })();
66 | ```
67 |
68 |
69 |
70 |
71 | ## How to iterate over the worker's incoming data (from the invoker perspective)
72 |
73 | ### main.js
74 | ```js
75 | import { AIWorker } from 'aww';
76 |
77 | const delay = (ms, v) => new Promise(res => setTimeout(res, ms, v));
78 |
79 | ;(async () => {
80 |
81 | const myWorker = new AIWorker(new Worker('./worker.js'));
82 |
83 | // will handle onmessage events and other stuff
84 | // it's an async iterable
85 | for await(const msg of myWorker) {
86 | console.log(`${msg.data} received inside main`);
87 | await delay(2000, msg); // fake long task
88 |
89 | // next messages from the main will be handled by the async iteration
90 | // no sense making this awaitable
91 | // DO NOT USE postMessage!
92 | myWorker.reply("It's all good from main!");
93 | }
94 |
95 | })();
96 | ```
97 |
98 | ### worker.js
99 | ```js
100 | import { AISource } from 'aww';
101 |
102 | const delay = (ms, v) => new Promise(res => setTimeout(res, ms, v));
103 |
104 | ;(async () => {
105 |
106 | const source = new AISource();
107 |
108 | let i = 0;
109 | while(true) {
110 | await delay(1000);
111 |
112 | // you can await the response from the main, but it is not mandatory
113 | await source.postMessage(i++).then(({data}) => console.log(data));
114 |
115 | /*
116 | alternative syntax
117 | const { data } = await source.postMessage(i++);
118 | console.log(data);
119 | */
120 | }
121 |
122 | })();
123 | ```
124 |
125 |
126 |
127 |
128 | ## How to iterate over both at the same time
129 |
130 | ### main.js
131 | ```js
132 | import { AIWorker } from 'aww';
133 |
134 | const delay = (ms, v) => new Promise(res => setTimeout(res, ms, v));
135 |
136 | ;(async () => {
137 |
138 | const myWorker = new AIWorker(new Worker('./worker.js'));
139 |
140 | // start the iteration posting a message
141 | // because both the main and the worker are waiting to start the iteration
142 | // not awaitable because the subsequent async iteration will handle worker's messages
143 | // DO NOT USE postMessage!
144 | myWorker.reply("Please start!");
145 |
146 | for await(const msg of myWorker) {
147 | console.log(`${msg.data} received inside main`);
148 | await delay(1000, msg); // fake long task
149 |
150 | // next messages from the main will be handled by the async iteration
151 | // no sense making this awaitable
152 | // DO NOT USE postMessage!
153 | myWorker.reply("It's all good from main!");
154 | }
155 |
156 | })();
157 | ```
158 |
159 | ### worker.js
160 | ```js
161 | import { AISource } from 'aww';
162 |
163 | const delay = (ms, v) => new Promise(res => setTimeout(res, ms, v));
164 |
165 | ;(async () => {
166 |
167 | const source = new AISource();
168 |
169 | for await(const msg of source) {
170 | console.log(`${msg.data} received inside worker`);
171 | await delay(2000, msg); // fake long task
172 |
173 | // next messages from the main will be handled by the async iteration
174 | // no sense making this awaitable
175 | // DO NOT USE postMessage!
176 | source.reply("It's all good from worker!");
177 | }
178 |
179 | })();
180 | ```
181 |
182 |
183 |
--------------------------------------------------------------------------------