Web worker Integration - javascript

I want to use web worker to handle my zipcode checker function, I haven't worked with web worker before so the concept is new to me
This is my zipcode function
``
function checkZipCode() {
event.preventDefault();
if(document.getElementById('zipcode').value < 20000) {
document.getElementById('zip-result').innerHTML = 'Sorry, we haven’t expanded to that area yet';
} else if (document.getElementById('zipcode').value >= 20000) {
document.getElementById('zip-result').innerHTML = 'We’ve got your area covered!'
} else {
return null
}
};

As per the docs workers are pretty easy to spin up:
//in a JS file
const myWorker = new Worker('./myWorker.js');//worker requested and top-level scope code executed
myWorker.postMessage('hello');
myWorker.addEventListener('message', e => {
//e.data will hold data sent from worker
const message = e.data;
console.log(message); // HELLO
//if it's just a one-time thing, you can kill the worker
myWorker.terminate();
}
myWorker.addEventListener('error', e => {//worker might throw an error
e.preventDefault();
console.log(e.message, `on line ${e.lineno}`);
});
//myWorker.js
//run whatever you need, just no DOM stuff, no window etc
console.log('this line runs when worker loads');
addEventListener('message', (e) => {
postMessage(e.data.toUpperCase());//up-case message and send it right back
});

Related

Accessing Service Worker saved IndexedDB data from Content Script via Chrome Runtime Messaging

In a Chrome Extension, I have no problem adding, updating, and removing data to/from an IndexedDB database accessed by my service worker with Chrome Runtime Messaging sent from my content script. My trouble is doing a full table read from my content script. I do a console.log() to dump out the property before I send it back in my sendResponse in the Chrome Runtime Messaging, and I see the data there properly, but the content script receives an undefined. I assume this is because of the asynchronous nature of getting the data. I tried promises and async/await and the combination thereof and I just can't seem to get anything except an undefined in my content script on the message back from the service worker. I also ran sending a test array back and that worked just fine -- but receiving the IndexedDB table data does not work in the message passing. I also tried to JSONify the data and that didn't help either. What's the catch?
service-worker.js
importScripts('modules/idb.js');
var SW = {};
SW.onReady = function(){
chrome.runtime.onMessage.addListener(function(o, sender, sendResponse) {
(o.readTable) && sendResponse(SW.readTable(o,sender));
});
};
SW.readTable = function(o,sender){
var sTable = o.table;
new Promise((resolve) => {
IDB.readTable(sTable,function(asEntries){
resolve(asEntries);
});
}).then((asEntries) => {
console.log('SW asEntries',asEntries); // this shows me valid data in tests
var o = {};
// can also change this to fake data with asEntries being a string array and bug goes away in content.js
o.response = asEntries;
return o;
});
};
SW.onReady();
modules/idb.js
var IDB = {};
// Requires storage (or, even better, unlimitedStorage) permission in your manifest.json file.
// Note also that dev console of service worker will not show data -- have to use toolbar button popup panel (if you have one) and
// dev console from there, or code to access it, which sucks.
IDB.connectStore = function(sTable,sReadWriteSetting,fn){
var conn = indexedDB.open('unlimitedStorage', 1);
conn.onupgradeneeded = function(e) {
var db = e.target.result;
db.createObjectStore(sTable);
};
conn.onsuccess = function(e) {
var db = e.target.result;
var tx = db.transaction(sTable,sReadWriteSetting);
var store = tx.objectStore(sTable);
fn(db,tx,store);
};
};
IDB.addToTable = function(sTable,sKey,sVal){
IDB.connectStore(sTable,'readwrite',function(db,tx,store){
if ((sKey === undefined) || (sKey === '') || (sKey === null) || (sKey === false)) { // auto key by increment
var req = store.count();
req.onsuccess = function(e){
sKey = e.target.result + 1;
store.add(sVal,sKey);
tx.complete;
}
} else {
store.add(sVal,sKey);
tx.complete;
}
});
};
IDB.removeFromTable = function(sTable,sKey){
IDB.connectStore(sTable,'readwrite',function(db,tx,store){
store.delete(sKey);
tx.complete;
});
};
IDB.readTableByKey = function(sTable,sKey,fn){
IDB.connectStore(sTable,'readonly',function(db,tx,store){
var req = store.get(sKey);
req.onerror = function(e){
fn(e.target.result);
}
req.onsuccess = function(e){
fn(e.target.result);
}
});
};
IDB.readTable = function(sTable,fn){
IDB.connectStore(sTable,'readonly',function(db,tx,store){
var req = store.getAll();
req.onerror = function(e){
fn(e.target.result);
}
req.onsuccess = function(e){
fn(e.target.result);
}
});
};
content.js
var CONTENT = {};
CONTENT.onReady = function(){
var o = {};
o.readTable = true;
o.table = 'loadTimes';
chrome.runtime.sendMessage(o,function(response){
if (response.response) { // errors here with response property being undefined
console.log('CONTENT RCVD asEntries',response.response);
}
});
};
CONTENT.onReady();
Chrome extensions API, unlike Firefox WebExtensions, can't handle Promise returned from a callback or provided in sendResponse, https://crbug.com/1185241.
There's also a bug in your readTable: you need to add return before new Promise((resolve)
The solution is two-fold:
Use return true from the callback to allow asynchronous sendResponse
Call sendReponse inside .then of a Promise chain.
chrome.runtime.onMessage.addListener(function(o, sender, sendResponse) {
if (o.readTable) {
SW.readTable(o,sender).then(sendResponse);
return true;
} else {
sendResponse(); // Chrome 99-101 bug workaround, https://crbug.com/1304272
}
});
Do not use this answer. It is here for posterity reasons and is just a workaround. The chosen solution works.
The fix is to return data in a different message thread:
In the service worker in SW.readTable(), just return variable o with o.response = true and then ignore the response in the content script.
Before returning the variable o from SW.readTable(), do a chrome.runtime.sendMessage({readTableResult = true, data: asEntries},function(response){ /* ignore response */});
In the content script, ignore any response back from the readTable message. So, the if (response.response) {...} condition can be eliminated.
In the content script, add a message listener with chrome.runtime.onMessage.addListener(o, sender, sendResponse) and look for the condition (o.readTableResult). Once received, the o.data will now contain the asEntries data.

Timer on the client side. Firebase + Angular

I'm doing an online auction. Accordingly, a timer is needed that, after the expiration of time, will change the status of the item to inactive. With the help of a web worker, I'm managed to do this. I check the end date (which I take from the firestore) with the current one and if they match, then I change the status. But unfortunately, it only works by pressing a button on the page that launches the webworker. If I change, for example, to onload, then ngOnInit does not have time to receive data from the firestore and, accordingly, the webworker is not executed. Is there a way that can help solve this problem?
I know that firebase has such a thing named 'function', but unfortunately it is paid.
component.ts
ngOnInit(){
this.initializeWorker();
}
timeWorker(){
if(this.products.status==='active'){
this.worker.postMessage(this.products.closureDate);
this.worker.addEventListener('message', ({data})=>{
this.checkStatus(data);
})
}
}
initializeWorker(){
if (typeof Worker !== 'undefined') {
// Create a new
this.worker = new Worker(new URL('./random.worker', import.meta.url));
this.worker.postMessage('');
} else {
// Web workers are not supported in this environment.
// You should add a fallback so that your program still executes correctly.
}
}
worker.ts
addEventListener('message', ({ data }) => {
console.log("data:" + data);
const response = endUpAuc(data);
postMessage(response);
});
function endUpAuc(ps) {
console.log("ps: "+ps);
var cDate = new Date(ps);
console.log("cdate: "+cDate)
if(cDate.getTime()>new Date().getTime())
{
console.log(cDate.getTime());
console.log(new Date().getTime());
console.log("active");
return "active";
}
else if(cDate.getTime()<=new Date().getTime()){
console.log("inactive");
//this.updateStatus("inactive");
return "inactive";
//this.router.navigate(['/results'])
}
}
component.html
<div class="about" onload="timeWorker()">
...
</div>

Calling a class function from within another class function - JS newb

This is probably more of a javascript question but I dont really know javascript in general, so im hoping its a simple fix.
So basically I am using two libraries that I want to work together. The beacon class listens for a bluetooth LE signal on a raspberry pi, at which point the onAdvertisement fucntion is called from the node-beacon-scanner library.
When this happens I want to publish a message to a message broker using MQTT (the other library), which should be a case of just calling client.publish(topic, message).
THe publish function works when I use it outside of the scanner functions, however if I call it inside one of the scanner functions such as onAdvertisement, it doesnt work, but also doesnt throw any errors.
I think it is an issue of me not understanding encapsualtion in node.js regarding modules.
Basically I want to know is there somthign im missing when calling the client.publish function from within the scanner.onadvertisement function? In the code below the publish call has been put in a function called pubBe.
Thankyou in advance!
const noble = require('#abandonware/noble');
const BeaconScanner = require("node-beacon-scanner");
const mqtt = require('mqtt');
var client = mqtt.connect('http://localhost:1883');
var scanner = new BeaconScanner();
var ledOn = false
client.on('connect', function () {
client.subscribe('led', function (err) {
if (!err) {
}
})
})
client.on('message', function (topic, message) {
// message is Buffer
console.log(message.toString())
client.end()
})
scanner.onadvertisement = (advertisement) => {
var beacon = advertisement["rssi"]
}
scanner.startScan().then(() => {
console.log("Scanning has started...")
pubBe(-10)
}).catch(error => {
console.error(error);
})
function pubBe(rssi){
if( rssi > -40 && !ledOn){
console.log('Turn LED on')
ledOn = true
payload = {"level": 100}
client.publish('led', JSON.stringify(payload))
}else if ( rssi < -40 && ledOn){
console.log('Turn LED off')
ledOn = false
payload = {"level": 0}
client.publish('led', JSON.stringify(payload))
}
}

What should I do with the redundant state of a ServiceWorker?

I gotta a companion script for a serviceworker and I'm trialling right now.
The script works like so:
((n, d) => {
if (!(n.serviceWorker && (typeof Cache !== 'undefined' && Cache.prototype.addAll))) return;
n.serviceWorker.register('/serviceworker.js', { scope: './book/' })
.then(function(reg) {
if (!n.serviceWorker.controller) return;
reg.onupdatefound = () => {
let installingWorker = reg.installing;
installingWorker.onstatechange = () => {
switch (installingWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
updateReady(reg.waiting);
} else {
// This is the initial serviceworker…
console.log('May be skipwaiting here?');
}
break;
case 'waiting':
updateReady(reg.waiting);
break;
case 'redundant':
// Something went wrong?
console.log('[Companion] new SW could not install…')
break;
}
};
};
}).catch((err) => {
//console.log('[Companion] Something went wrong…', err);
});
function updateReady(worker) {
d.getElementById('swNotifier').classList.remove('hidden');
λ('refreshServiceWorkerButton').on('click', function(event) {
event.preventDefault();
worker.postMessage({ 'refreshServiceWorker': true } );
});
λ('cancelRefresh').on('click', function(event) {
event.preventDefault();
d.getElementById('swNotifier').classList.add('hidden');
});
}
function λ(selector) {
let self = {};
self.selector = selector;
self.element = d.getElementById(self.selector);
self.on = function(type, callback) {
self.element['on' + type] = callback;
};
return self;
}
let refreshing;
n.serviceWorker.addEventListener('controllerchange', function() {
if (refreshing) return;
window.location.reload();
refreshing = true;
});
})(navigator, document);
I'm a bit overwhelmed right now by the enormity of the service workers api and unable to "see" what one would do with reg.installing returning a redundant state?
Apologies if this seems like a dumb question but I'm new to serviceworkers.
It's kinda difficult to work out what your intent is here so I'll try and answer the question generally.
A service worker will become redundant if it fails to install or if it's superseded by a newer service worker.
What you do when this happens is up to you. What do you want to do in these cases?
Based on the definition here https://www.w3.org/TR/service-workers/#service-worker-state-attribute I am guessing just print a log in case it comes up in debugging otherwise do nothing.
You should remove any UI prompts you created that ask the user to do something in order to activate the latest service worker. And be patient a little longer.
You have 3 service workers, as you can see on the registration:
active: the one that is running
waiting: the one that was downloaded, and is ready to become active
installing: the one that we just found, being downloaded, after which it becomes waiting
When a service worker reaches #2, you may display a prompt to the user about the new version of the app being just a click away. Let's say they don't act on it.
Then you publish a new version. Your app detects the new version, and starts to download it. At this point, you have 3 service workers. The one at #2 changes to redundant. The one at #3 is not ready yet. You should remove that prompt.
Once #3 is downloaded, it takes the place of #2, and you can show that prompt again.
Write catch function to see the error. It could be SSL issue.
/* In main.js */
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./sw.js')
.then(function(registration) {
console.log("Service Worker Registered", registration);
})
.catch(function(err) {
console.log("Service Worker Failed to Register", err);
})
}

CometD taking more time in pushing messages

I am trying to implement CometD in our application. But it is taking more time compared to the existing implementation in our project. The existing system is taking time in milliseconds where as CometD is taking 2 seconds to push the message.
I am not sure where I am going wrong. Any guidance will help me lot.
My code:
Java script at client side
(function($)
{
var cometd = $.cometd;
$(document).ready(function()
{
function _connectionEstablished()
{
$('#body').append('<div>CometD Connection Established</div>');
}
function _connectionBroken()
{
$('#body').append('<div>CometD Connection Broken</div>');
}
function _connectionClosed()
{
$('#body').append('<div>CometD Connection Closed</div>');
}
// Function that manages the connection status with the Bayeux server
var _connected = false;
function _metaConnect(message)
{
if (cometd.isDisconnected())
{
_connected = false;
_connectionClosed();
return;
}
var wasConnected = _connected;
_connected = message.successful === true;
if (!wasConnected && _connected)
{
_connectionEstablished();
}
else if (wasConnected && !_connected)
{
_connectionBroken();
}
}
// Function invoked when first contacting the server and
// when the server has lost the state of this client
function _metaHandshake(handshake)
{
if (handshake.successful === true)
{
cometd.batch(function()
{
cometd.subscribe('/java/test', function(message)
{
$('#body').append('<div>Server Says: ' + message.data.eventID + ':'+ message.data.updatedDate + '</div>');
});
});
}
}
// Disconnect when the page unloads
$(window).unload(function()
{
cometd.disconnect(true);
});
var cometURL = "http://localhost:8080/cometd2/cometd";
cometd.configure({
url: cometURL,
logLevel: 'debug'
});
cometd.addListener('/meta/handshake', _metaHandshake);
cometd.addListener('/meta/connect', _metaConnect);
cometd.handshake();
});
})(jQuery);
Comet service class
#Listener("/service/java/*")
public void processMsgFromJava(ServerSession remote, ServerMessage.Mutable message)
{
Map<String, Object> input = message.getDataAsMap();
String eventId = (String)input.get("eventID");
//setting msg id
String channelName = "/java/test";
// Initialize the channel, making it persistent and lazy
bayeux.createIfAbsent(channelName, new ConfigurableServerChannel.Initializer()
{
public void configureChannel(ConfigurableServerChannel channel)
{
channel.setPersistent(true);
channel.setLazy(true);
}
});
// Publish to all subscribers
ServerChannel channel = bayeux.getChannel(channelName);
channel.publish(serverSession, input, null);
}
Is there any thing I need to change in server side code.
You have made your channel lazy, so a delay in message broadcasting is expected (that is what lazy channels are all about).
Please have a look at the documentation for lazy channels.
If you want immediate broadcasting don't set the channel as lazy.

Categories

Resources