Why is the web worker api not working in react even in the simplest implementation? - fetch-api

I created a toy mern app which can upload images to mongodb. It works fine in the development but after I deploy to heroku, I get a timeout error. After doing a fair amount of investigations I come to the conclusion that web workers will help sort this out. So I've done this:
This is a snippet from the component that takes care of sending this request, to upload an image:
const handleUploadPhoto = evt => {
evt.preventDefault();
const worker = new Worker('imageUpload.js');
worker.postMessage(selectedPhotos);
worker.onmessage = evt => console.log(evt.data);
closeModal();
};
This whole component is actually a modal, and after I submit this handleUploadPhoto function the modal closes. Notice the closeModal function;
In the same folder as this component I have the imageUpload.js worker file:
self.addEventListener('message', evt => {
const { selectedPhotos } = evt.data;
selectedPhotos.map(photo => {
const formData = new FormData();
formData.append('photo', photo);
fetch( `/api/photo/upload/${JSON.parse(localStorage.getItem('loggedUser'))._id}`, {
method: 'POST',
body: formData,
headers: ['Content-Type', 'multipart/form-data'],
}).then(() => console.log('OK'));
});
self.postMessage(evt.data);
});
The idea behind this functionality is that I select one or more images, then I map through them and send a post request to the server with each image as formData. So I thought to move the request to a worker so that it doesn't break my UI anymore. Needless to say that it doesn't work and it gives net::EMPTY_RESPONSE error after some seconds. What I also noticed is that the worker doesn't communicate with the file it was issued in at all. I tried to make a text message pop in the console:
const handleUploadPhoto = evt => {
evt.preventDefault();
const worker = new Worker('imageUpload.js');
worker.postMessage('Hello');
worker.onmessage = evt => console.log(evt.data);
closeModal();
};
In the same folder as this component I have the imageUpload.js worker file:
self.addEventListener('message', evt => {
self.postMessage(evt.data + ' world');
});
It doesn't work. I tried the same approach in a separate project and it worked.
How can I make an HTTP request do what it's supposed to do from a web worker?

Related

Service workers "sync" operation is working while its offline?

I have a PWA project where I send the data to server. During this process, if the user is offline then the data is stored in indexedDb and a sync tag is registered. So, then when the user comes online that data can sent to the server.
But In my case the sync event gets executed immediately when the we register a sync event tag, which means the data is tried to be sent to server while its offline, which is not going to work.
I think the sync event supposed to fire while its online only, what could be issue here ?
The service worker's sync event works accordingly when I tried to enable and disable the offline option of chrome devtools, and also works correctly in my android phone.
This is how I register my sync tag
function onFailure() {
var form = document.querySelector("form");
//Register the sync on post form error
if ('serviceWorker' in navigator && 'SyncManager' in window) {
navigator.serviceWorker.ready
.then(function (sw) {
var post = {
datetime1: form.datetime1.value,
datetime: form.datetime.value,
name: form.name.value,
image: form.url.value,
message: form.comment.value
};
writeData('sync-comments', post)
.then(function () {
return sw.sync.register('sync-new-comment');
})
.then(function () {
console.log("[Sync tag registered]");
})
.catch(function (err) {
console.log(err);
});
});
}
}
And this is how the sync event is called
self.addEventListener('sync', function (event) {
console.log("[Service worker] Sync new comment", event);
if (event.tag === 'sync-new-comment') {
event.waitUntil(
readAllData('sync-comments')
.then(function (data) {
setTimeout(() => {
data.forEach(async (dt) => {
const url = "/api/post_data/post_new_comment";
const parameters = {
method: 'POST',
headers: {
'Content-Type': "application/json",
'Accept': 'application/json'
},
body: JSON.stringify({
datetime: dt.datetime,
name: dt.name,
url: dt.image,
comment: dt.message,
datetime1: dt.datetime1,
})
};
fetch(url, parameters)
.then((res) => {
return res.json();
})
.then(response => {
if (response && response.datetimeid) deleteItemFromData('sync-comments', response.datetimeid);
}).catch((error) => {
console.log('[error post message]', error.message);
})
})
}, 5000);
})
);
}
});
you mention
The service worker's sync event works accordingly when I tried to enable and disable the offline option of chrome devtools, and also works correctly in my android phone.
So I'm not sure which case is the one failing.
You are right that the sync will be triggered when the browser thinks the user is online, if the browser detects that the user is online at the time of the sync registration it will trigger the sync:
In true extensible web style, this is a low level feature that gives you the freedom to do what you need. You ask for an event to be fired when the user has connectivity, which is immediate if the user already has connectivity. Then, you listen for that event and do whatever you need to do.
Also, from the workbox documentation
Browsers that support the BackgroundSync API will automatically replay failed requests on your behalf at an interval managed by the browser, likely using exponential backoff between replay attempts.

busboy-bodyparser changes my request so that GridFsStorage doesn't register the request-data in mongodb

I am a frontend developer trying to broaden my horizons, and making what will become a MERN application. I'm struggling with image uploads to mongodb.
First I used the express bodyparser:
app.use(express.urlencoded({ extended: true }));
and app.use(express.json());
when used like this I managed to upload the file fine, and the uploaded file showed up in MongoDB Compass.
I found out that this doesn't support multipart/form-data, so I've changed the bodyparser to busboy-bodyparser so that I can access both form-data and the file that is being uploaded. So I changed the bodyparser to:
app.use(busboyBodyParser());
and now it won't upload the request-data to mongodb.
My upload control looks like this:
const upload = require("../middleware/upload");
const uploadFile = async (req, res) => {
try {
req.file = req.files.file;
await upload(req, res);
if (req.file == undefined) {
return res.send(`You must select a file.`);
}
return res.send(`File has been uploaded.`);
} catch (error) {
console.log(error);
return res.send(`Error when trying upload image: ${error}`);
}
};
module.exports = {
uploadFile: uploadFile
};
the reason I've set req.file equals to req.files.file is because busboy-bodyparser sends the file from req.files.file and not req.file, and I thought that this change would make the request function properly, it did not.
My upload-middleware looks like this:
const promise = mongoose.connect(mongoURI, { useNewUrlParser: true, useUnifiedTopology: true });
const conn = mongoose.connection;
let gfs;
conn.once('open', () => {
gfs = Grid(conn, mongoose.mongo);
gfs.collection('uploads');
});
//create storage object
const storage = new GridFsStorage({
db: promise,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString('hex') + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: 'uploads',
metadata: {
title: req.body.title,
orientation: req.body.orientation
}
};
resolve(fileInfo);
});
});
}
});
const uploadFile = multer({ storage }).single("file");
var uploadFilesMiddleware = util.promisify(uploadFile);
module.exports = uploadFilesMiddleware;
I believe this is the code that logs (node:15124) DeprecationWarning: Listening to events on the Db class has been deprecated and will be removed in the next major version.
(Use node --trace-deprecation ... to show where the warning was created)
which is another problem I'm unsure how to solve, but that's another problem for another day.
My end goal is to be able to send the file to mongodb, with the attached metadata (title and orientation).
with this code I'm able to get the "File has been uploaded" message from the upload-control, but in mongoDB compass no file/chunks has been uploaded. The uploads worked great on file-uploads (without the metadata) with the express-bodyparser, so when I changed that to the busboy-bodyparser I get both the file and the metadata as intended but it is not loaded into the db, which leads me to believe that the new bodyparser changes the request so that GridFsStorage no longer recognizes it and doesn't put the data into the db. But frankly I'm just speculating here, and I generally have very limited knowledge of backend.
I use the correct enctype on the form I believe:
<form
action="/upload"
method="POST"
enctype="multipart/form-data">
any tips or explanations is very much appreciated!
I am a complete beginner in backend, so don't be afraid to spell it our for me :)
I managed to fix it!
I'm unsure what caused it, but I believe that the req.body-fields hadn't been populated yet or something of that nature. I therefore switched out
metadata: {
title: req.body.title,
orientation: req.body.orientation
}
with: metadata: req.body and it just works.
For any other backend-newbie who might stumble upon this, also remember to name your inputs in html like this: <input name="title" type="text" /> it is the name-attribute that gets submitted with the html-form and gives the key to req.body, so that you can access for example req.body.title (which didn't work here, but still worth knowing)

Why does pusher in a service worker disconnect every minute?

my website refreshs let's say every 5 minutes and I use a service worker. Inside this service worker I use pusher.com and this happens:
There are many "Connection" and "Disconnection" events for only two Clients (Raspberry Pi). After this "Vacated" event there is no re-subscription.
Here is what every site does:
if ('serviceWorker' in navigator) { // Make sure sw are supported
window.addEventListener('load', () => {
navigator.serviceWorker
.register('/sw.js')
.then(reg => {
console.log('Service Worker: Registered');
})
.catch(err => console.log(`Service Worker: Error: ${err}`));
});
}
And this happens only once (and not on activating) in service worker:
var channel = pusher.subscribe('web-' + channel);
const swListener = new BroadcastChannel('swListener');
channel.bind('foo', function() {
self.foo();
});
If I run this code above on every page request everything get worse.
Did I something wrong? Is there a way to check my actual subscriptions in my service worker?

Name with gear icon in Chrome network requests table

(30119999.xml in pic1, has a gear mark pre name)
I set this request in Web Worker, response data is ok and I terminated it in onmessage callback
but why the request always in pending and can't preview, please help.
pseudocode:
const workerBlob = new Blob([`onmessage = function (event) {
const xhr = new XMLHttpRequest();
xhr.addEventListener('load', function () {
postMessage(xhr.response);
});
xhr.open('GET', event.data.url, true);
xhr.send();
}`], { type: 'application/javascript' });
const workerURL = URL.createObjectURL(workerBlob);
const worker = new Worker(workerUrl);
worker.postMessage({url});
worker.onmessage = (message) => {
// do something
worker.terminate();
};
According to Debugging Service Workers :
The gear icon signifies that these requests came from the Service
Worker itself. Specifically, these are the requests being made by the
Service Worker's install handler to populate the offline cache.
Make sure you are not terminating your worker too fast. You should terminate the Worker when the loadend event has fired.

jupyter-js-services - how to save notebook

I'm trying to use jupyter as a backend for my system and now I play with examples from jupyter-js-api docs.
Using IKernel and INotebookSession I managed to execute simple code and get the response form kernel.
But I can's figure out how to extract the notebook itself. there's nothing like "saveNotebook()" in API. I try to execute session.renameNotebook(), it completes successfully, but no files appear in filesystem (tried different paths like "/tmp/trynote.ipynb" "trynote.ipnb" and so on...).
Here's the code, it is slightly edited example from http://jupyter.org/jupyter-js-services/ page
#!/usr/bin/env node
var jpt = require("jupyter-js-services");
var xr = require("xmlhttprequest");
var ws = require("ws");
global.XMLHttpRequest = xr.XMLHttpRequest;
global.WebSocket = ws;
// start a new session
var options = {
baseUrl: 'http://localhost:8889',
wsUrl: 'ws://localhost:8889',
kernelName: 'python',
notebookPath: 'trynote.ipynb'
};
jpt.startNewSession(options).then((session) => {
// execute and handle replies on the kernel
var future = session.kernel.execute({ code: 'print(5 * 5);' });
future.onDone = (msg) => {
console.log('Future is fulfilled: ');
console.log(msg);
};
future.onIOPub = (msg) => {
console.log("Message in IOPub: ");
console.log(msg);
};
// rename the notebook
session.renameNotebook('trynote2.ipynb').then(() => {
console.log('Notebook renamed to', session.notebookPath);
});
// register a callback for when the session dies
session.sessionDied.connect(() => {
console.log('session died');
});
// kill the session
session.shutdown().then(() => {
console.log('session closed');
});
});
Looking and ContentManager API it seems to work with already existing files, or creating new ones, but its unclear how is it bound to sessions.
More, even simplest try to use "newUntitled" function gives 404 response...
var contents = new jpt.ContentsManager('http://localhost:8889');
// create a new python file
contents.newUntitled("foo", { type: "file", ext: "py" }).then(
(model) => {
console.log(model.path);
}
);
I feel a bit disoriented with all this and would appreciate any explanations.
Thanks..