Skip to content

Instantly share code, notes, and snippets.

@barelyhuman
Created June 29, 2021 20:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save barelyhuman/d33a3b5882cfd6952491268782a7937b to your computer and use it in GitHub Desktop.
Save barelyhuman/d33a3b5882cfd6952491268782a7937b to your computer and use it in GitHub Desktop.
Make API calls synchronous in web workers using atomics
// Simple js file that index.html has as a script src or the entry file of the app
let worker = new Worker("worker.js");
worker.postMessage({
url: "https://jsonplaceholder.typicode.com/todos/1",
});
// just fetch returning text
const fetcher = (url) => {
return fetch(url).then((data) => data.text());
};
// Performing the needed async operation , this worker will run await on the needed method
const performAsync = async (url, sharedDataChan) => {
const data = await fetcher(url);
var valueStore = new Int32Array(sharedDataChan);
for (var i = 0, strLen = data.length; i < strLen; i++) {
valueStore[i] = data.charCodeAt(i);
}
};
/*
listener - will wait for the first notification from the
main worker and then start processing the needed data
then it handles the needed task in the performAsync call
*/
const listener = async ({ notifierChan, sharedDataChan, url }) => {
const notifierChannelMem = new Int32Array(notifierChan);
Atomics.wait(notifierChannelMem, 1, 1);
await performAsync(url, sharedDataChan);
Atomics.store(notifierChannelMem, 0, 1);
Atomics.notify(notifierChannelMem, 0, 1);
};
/*
The initial message sent to everyone that are waiting on syncer that syncer
is now ready to listen for your data
*/
onmessage = (msg) => {
postMessage(0);
const { data } = msg;
listener(data);
};
let syncer = new Worker("syncer.js");
// Channel to handle both the workers to trigger each other
let notifierChan = new SharedArrayBuffer(8);
// Channel the data will be shared on, in this case the API request data
let sharedDataChan = new SharedArrayBuffer(10000);
// ArrayBuffer to string
function bufferToString(buf) {
return String.fromCharCode
.apply(null, new Uint16Array(buf))
.replace(/\u0000/g, "");
}
// Function that notifies the child web worker to start doing the async task
// and then waits for it to complete
const loadUrl = () => {
const notifierChannelMem = new Int32Array(notifierChan);
const sharedArray = new Int32Array(sharedDataChan);
Atomics.notify(notifierChannelMem, 1, 1);
Atomics.wait(notifierChannelMem, 0, 0);
return bufferToString(sharedArray);
};
// Wrapper function for loadUrl , you can just use loadUrk directly
const run = () => {
console.log(loadUrl());
};
// onmessage handler to get requests from the main thread
onmessage = (fromMain) => {
/*
Send the initial shared data channels before triggering a wait so the writer and
reader , in this case
writer - syncer.js
reader - worker.js
can actually be 2 threads and ready to actually handle everything
the writer(syncer.js) is waiting for a start notification from this worker
before running the async operation
*/
syncer.postMessage({
notifierChan: notifierChan,
sharedDataChan,
url: fromMain.data.url,
});
// Trigger the handler once syncer says it's up
syncer.onmessage = (ev) => {
run();
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment