Call worker via postmessage from a class - javascript

I am currently learning how to handle with workers. I made a very simple example with a class.
All js files are in the src folder.
//***********index.html**************
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script type="module" src="./src/main.js"></script>
</body>
</html>
//***********main.js**************
import {test} from "./test.js";
new test.DataLoader();
//***********test.js***************
export const test = (() => {
class DataLoader{
constructor(){
this.worker = new Worker('./src/worker.js', {type: 'module'});
this.worker.onmessage = (e) => {
this.result = e.data;
console.log(this.result);
};
}//end constructor
}//end class
return {DataLoader};
})();
//***********worker.js***************
postMessage("kimba");
But if I want to trigger the worker via postMessage, it doesn't work. I'm sure it's not a big thing but i just don't see the error.
//***********test.js***************
export const test = (() => {
class DataLoader{
constructor(){
this.worker = new Worker('./src/worker.js', {type: 'module'});
this.worker.onmessage = (e) => {
this.result = e.data;
console.log(this.result);
};
//this is new, call postMessage method
this.postMessage("kimba");
}//end constructor
//this is new, a postMessage method
postMessage(msg){
this.worker.postMessage(msg);
}
}//end class
return {DataLoader};
})();
//***********worker.js***************
self.onmessage = (msg) => {
self.postMessage(msg);
};
Exactly as above, but this time the worker should not respond immediately when it is created, but should wait for a postMessage from DataLoader. Of course, I would like to use this in a more complex environment, but posting 1000 lines here is clumsy. This simple example sums up my problem. I want to call the worker with postmessage from within DataLoader.

The tip with the self got me thinking. msg.data instead of msg alone in the worker.js and it works. Your tip with the self gave me the feeling that maybe one more prefix or postfix is ​​missing. I messed around with it for hours earlier and it wouldn't want to run and your tip put me on the right track. Thank you.
self.onmessage = (msg) => {
self.postMessage(msg.data);
};

Related

NodeJS tell open(html) to go first/tell other code to go last

I'm trying to determine if nodeJS is a viable solution for a work project at my internship. TLDR: I need to have my basicNode.js file wait until the open('./basic.html') has actually fully opened up the browser and loaded content before it calls any other code, either from basicNode.js itself or from change.js.
End goal/purpose of basicNode.js should be to open the html, and once it's loaded, call the changeThis() function from change.js to alter the html to show "Hello" instead of "Welcome to JavaScript".
I've tried mixing up the order of things or having some console.logs output current time etc before calling the change functions but I just can't seem to get open('./basic.html') to fully complete before any attempt is made to change html elements
basic.html
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>Date/Time: <span id="datetime"></span></p>
<p id="here">Welcome to JavaScript</p>
<form>
<input type="button" value="click" onclick="changeThis()"/>
</form>
</body>
<script>
var dt = new Date();
document.getElementById("datetime").innerHTML = dt.toLocaleString();
</script>
<script type="text/javascript" src="change.js"></script>
<!--
<script>waitForElementToDisplay("#here",function(){alert("Hi");},1000,9000);</script>
-->
</html>
change.js
function changeThis() {
document.getElementById("here").innerHTML = "Hello";
}
function waitForElementToDisplay(selector, callback, checkFrequencyInMs, timeoutInMs) {
var startTimeInMs = Date.now();
(function loopSearch() {
if (document.querySelector(selector) != null) {
callback();
return;
}
else {
setTimeout(function () {
if (timeoutInMs && Date.now() - startTimeInMs > timeoutInMs)
return;
loopSearch();
}, checkFrequencyInMs);
}
})();
}
function sendAlert() {
alert("Hi");
}
module.exports={sendAlert};
module.exports={changeThis};
module.exports={waitForElementToDisplay};
basicNode.js
const open = require('open');
var { change } = require('./change')
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
global.document = new JSDOM('./basic.html').window.document;
open('./basic.html');
change.waitForElementToDisplay("#here",change.sendAlert(),1000,9000);
change.changeThis();
If you're asking why it appears that open isn't "going first," open('./basic.html') is going first, it's just an asynchronous operation and returns a Promise. This is sort of an advanced topic for someone who is new to JS and Node, but if you want to run the change script after you've definitely opened the file, you'll need to await the promise. You can read all about this in the link to MDN above, and there is a ton of material online about this topic.
const open = require('open');
var { change } = require('./change')
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
global.document = new JSDOM('./basic.html').window.document;
const main = async () => {
await open('./basic.html');
change.waitForElementToDisplay("#here",change.sendAlert(),1000,9000);
change.changeThis();
}
main();
Note that I've wrapped the code in an async function because, for the moment, you can't use async/await syntax in the global scope. Alternatively, you could do as #JSmart523 indicated:
const open = require('open');
var { change } = require('./change')
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
global.document = new JSDOM('./basic.html').window.document;
open('./basic.html').then(() => {
change.waitForElementToDisplay("#here",change.sendAlert(),1000,9000);
change.changeThis();
});

How to use Facebook Instant Games Leaderboards and In-App Ads with LibGDX

I would like to make my LibGDX game work as a Facebook instant game. So far I have managed to set up the game client as shown in the quick start guide like this:
<!DOCTYPE html>
<html>
<head>
<title>My LibGDX Game</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<meta
id="gameViewport"
name="viewport"
content="width=device-width initial-scale=1"
/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="full-screen" content="yes" />
<meta name="screen-orientation" content="portrait" />
</head>
<body>
<div align="center" id="embed-html"></div>
<script src="https://connect.facebook.net/en_US/fbinstant.6.3.js"></script>
<script src="main.js"></script>
<script type="text/javascript" src="html/html.nocache.js"></script>
</body>
<script>
function handleMouseDown(evt) {
evt.preventDefault();
evt.stopPropagation();
window.focus();
}
function handleMouseUp(evt) {
evt.preventDefault();
evt.stopPropagation();
}
document
.getElementById("embed-html")
.addEventListener("mousedown", handleMouseDown, false);
document
.getElementById("embed-html")
.addEventListener("mouseup", handleMouseUp, false);
</script>
</html>
In my main.js file which is separate from the GWT generated JS code, I am able to use the Facebook Instant games SDK to get the username and ID like this:
FBInstant.initializeAsync().then(function () {
var progress = 0;
var interval = setInterval(function () {
progress += 3;
FBInstant.setLoadingProgress(progress);
if (progress >= 99) {
clearInterval(interval);
FBInstant.startGameAsync().then(function () {
console.log("shit stsrted");
var playerId = FBInstant.player.getID(); // Get Facebook ID
var playerName = FBInstant.player.getName(); // Get Facebook Name
console.log(playerId);
console.log(playerName);
});
}
}, 100);
console.log("loaded");
});
Now I need to show an Ad and leaderboard on the GameOver screen, but how can I add Leaderboards and In-App Ads If the code for that is in JavaScript? I tried to dig into the GWT compiled JS code to somehow find the GameOver function but that code is unreadable. Is there a way to do this in the Java code maybe inject JavaScript and let GWT compile it with Java?
GWT project supports calling native JS from Java, previously through JSNI which allowed you to write JavaScript as a comment in method in Java world, but now there is a better solution - JSInterop - http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJsInterop.html
I came across your question when learning about it myself, and this blog explained it well for my use case:
https://lofidewanto.medium.com/learn-once-use-everywhere-javascript-in-java-for-web-browser-ec1b9657232c
Here's what I did:
Libgdx is multiplatform and I want to preserve the ability to port my game to mobile platforms, so I create a Java interface that handles loading and displaying of Ads (interstitials and rewarded videos):
public interface AdRequestHandler {
void loadInterstitial();
void showInterstitial();
void loadRewardedVideo();
void showRewardedVideo();
}
My GWT/HTML will only target Facebook instant games, so I provide an implementation of above interface:
public class FacebookAdRequestHandler implements AdRequestHandler {
private FacebookAdRequestService facebookAdRequestService = new FacebookAdRequestService();
#Override
public void loadInterstitial() {
facebookAdRequestService .loadInterstitialImpl();
}
#Override
public void showInterstitial() {
facebookAdRequestHandlerNative.showInterstitialImpl();
}
#Override
public void loadRewardedVideo() {
facebookAdRequestService.loadRewardedVideoImpl();
}
#Override
public void showRewardedVideo() {
facebookAdRequestService.showRewardedVideoImpl();
}
}
The implementation simply calls a Service class that does all the native JS calls:
#JsType(namespace = JsPackage.GLOBAL, name = "FacebookAdRequestHandler", isNative = true)
public class FacebookAdRequestService {
public native void loadInterstitialImpl();
public native void showInterstitialImpl();
public native void loadRewardedVideoImpl();
public native void showRewardedVideoImpl();
}
now for the JS part, I take your main.js file in the project under html/webapp
FBInstant.initializeAsync().then(function () {
var progress = 0;
var interval = setInterval(function () {
progress += 3;
FBInstant.setLoadingProgress(progress);
if (progress >= 99) {
clearInterval(interval);
FBInstant.startGameAsync().then(function () {
//var playerId = FBInstant.player.getID(); // Get Facebook ID
//var playerName = FBInstant.player.getName(); // Get Facebook Name
});
}
}, 100);
});
FacebookAdRequestHandler = function () {
var preloadedInterstitial = null;
var preloadedRewardedVideo = null;
}
FacebookAdRequestHandler.prototype.loadInterstitialImpl = function () {
FBInstant.getInterstitialAdAsync(
'123123123123_123123123123' // Your Ad Placement Id
).then(function(interstitial) {
// Load the Ad asynchronously
preloadedInterstitial = interstitial;
return preloadedInterstitial.loadAsync();
}).then(function() {
console.log('Interstitial preloaded');
}).catch(function(err){
console.error('Interstitial failed to preload: ' + err.message);
});
}
FacebookAdRequestHandler.prototype.showInterstitialImpl = function () {
preloadedInterstitial.showAsync()
.then(function() {
// Perform post-ad success operation
console.log('Interstitial ad finished successfully');
})
.catch(function(e) {
console.error(e.message);
});
}
FacebookAdRequestHandler.prototype.loadRewardedVideoImpl = function () {
FBInstant.getRewardedVideoAsync(
'456456456456_456456456456' // Your Ad Placement Id
).then(function(rewarded) {
// Load the Ad asynchronously
preloadedRewardedVideo = rewarded;
return preloadedRewardedVideo.loadAsync();
}).then(function() {
console.log('Rewarded video preloaded');
}).catch(function(err){
console.error('Rewarded video failed to preload: ' + err.message);
});
}
FacebookAdRequestHandler.prototype.showRewardedVideoImpl = function () {
preloadedRewardedVideo.showAsync()
.then(function() {
// Perform post-ad success operation
console.log('Rewarded video watched successfully');
})
.catch(function(e) {
console.error(e.message);
});
}
I've taken the code for loading and showing the interstitial directly from FB's documentation for their Instant Games - In-App Ads for Instant Games
And I've actually taken your example to resolve an issue of initializing and going around the progress bar reporting.
I've tested it for interstitials so far and that displays the ad correctly. What needs work is making sure to only display the ad when it loaded (maybe just do a null check inside showInterstitialImpl.
Now this is only a one sided solution, it doesn't report back to the Java part that anything was displayed, you can do that as well using #JsFunction annotation to create some callbacks

I want to stop this running after 10 seconds

I tried using setTimeout(clearInterval(MY_INT, 10000) in the function olo() but it ran only once and stopped :/ Where could I put it or should I use for()?
There is no <script src></script> tag because I am using online editor. If you guys know how to make VScode like an online editor please suggest.
*
const btn = document.getElementById('Button');
const target = document.getElementById('here');
btn.addEventListener('click', ()=>{
function olo(){
let loadingImage = document.createTextNode('**');
target.appendChild(loadingImage);
}
const MY_INT = setInterval(olo ,1000)
})
<!DOCTYPE html>
<html>
<head>
<title>Loading...</title>
</head>
<body>
<div>
<button id='Button'>load</button>
<h1 id='here'></h1>
</div>
</body>
</html>
The 10000 argument should be in setTimeouts parameters, but you have it in clearInterval.
You should not place it in the olo function, it should be right after you set the interval.
setTimout expects a callback, you have provided it a function call, change clearInterval(MY_INT) to an anonymous function (or any function, but NOT a function call). e.g. () => clearInterval(MY_INT)
Full cleanup of your code with the fixes above (i tweaked the timings slightly to make it easier to play with):
btn.addEventListener("click", () => {
function olo() {
let loadingImage = document.createTextNode("*");
target.appendChild(loadingImage);
}
const MY_INT = setInterval(olo, 500);
setTimeout(() => clearInterval(MY_INT), 3000);
});
Sandbox with fix

A simple example using html, javascript and jQuery

I'm starting with javascript, websockets and jQuery developing a simple example. In the html page I only have a button, that, when pressed, has to send its state (ON/OFF for instance). In index html, I have the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"></meta>
<title>First App</title>
<link rel="stylesheet" href="css/style.css">
<script src="js/jquery-3.3.1.min.js"></script>
<script src="js/APP.js"></script>
</head>
<body>
<div id='hello_message'>
Connecting...
</div>
<button id='state'>Turn on</button>
<div id='off'>OFF</div>
<div id='on'>ON</div>
</body>
</html>
My intention is to open a websocket between the client and the server when the page is loaded, and keep it open for any information to be sent between both of them. To this end, I have the following file containing the js code (APP.js):
window.onload = APPStart;
// Page onload event handler
function APPStart() {
state = false;
if ("WebSocket" in window)
{
var ws = new WebSocket("ws://10.30.0.142:8020");
ws.onopen = function()
{
alert ("Connected");
$('#hello_message').text("Connected");
};
ws.onmessage = function (evt)
{
var received_msg = evt.data;
};
ws.onclose = function()
{
alert("Connection is closed...");
};
window.onbeforeunload = function(event) {
socket.close();
};
}
else
{
// The browser doesn't support WebSocket
alert("WebSocket NOT supported by your Browser!");
}}
Now, every time someone clicks on button, I would like to execute the following code:
// program checks if led_state button was clicked
$('#state').click(function() {
alert ("click");
// changes local led state
if (led_state == true){
$('#on').hide();
$('#off').show();
state = false;
ws.send("ON");
}
else{
$('#off').hide();
$('#on').show();
state = true;
ws.send("OFF");
}
});
I've tried to put this part of the code inside the function APPStart, but it doesn't work. I also suspect that jQuery is not working either since messages are not updated. Any suggestion to make it work?
Thanks for the comments. The code works, the problem was in the cache of the browser. Once I noticed it, I cleaned the cache and everything started to work. Silly me.

Can I wait for a child window to load the second page?

There are three accessible pages, http://localhost/parent.html, http://localhost/child1.html and http://localhost/child2.php.
Only parent.html is editable for me, whereas the others are not.
child2.php is accessible only through a POST request from child1.html.
What I want to do is to automate extracting data from every child page.
I'm on parent.html now and will access the other two pages from here using a child window.
However, the code which waits for loading the second page doesn't work.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>parent.html</title>
<script>
function waitForLoading(targetWindow) {
return new Promise((resolve, reject) => {
targetWindow.addEventListener("load", resolve);
});
}
document.addEventListener("DOMContentLoaded", async () => {
const childWindow = open("child1.html", "_blank");
await waitForLoading(childWindow);
// do something with DOM
childWindow.document.querySelector("form[action='child2.php']").submit();
await waitForLoading(childWindow); // getting stuck here
// do something with DOM // so, this line is unreachable
});
</script>
</head>
<body>
<h1>parent.html</h1>
</body>
</html>
My questions are:
Why does the code get stuck?
How do I wait for a child window to load the second page?
If it is impossible, do you know any workarounds to achieve the goal?
Thanks.
Why does the code get stuck?
Even if a child window loads another page, it seems not to run load or DOMContentLoaded for the second time or later.
How do I wait for a child window to load the second page?
Loop and watch the loading state of the page.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>parent.html</title>
<script>
function waitFirstPage(targetWindow) {
return new Promise((resolve, reject) => {
targetWindow.addEventListener("load", resolve);
});
}
function movePage(targetWindow, action) {
const originalDocument = targetWindow.document;
action();
return new Promise((resolve, reject) => {
(function loop() {
if (targetWindow.document !== null
&& targetWindow.document !== originalDocument
&& targetWindow.document.readyState === "complete") {
resolve();
}
setTimeout(loop, 100);
})();
});
}
document.addEventListener("DOMContentLoaded", async () => {
const childWindow = open("child1.html", "_blank");
await waitFirstPage(childWindow);
// do something with DOM
await movePage(childWindow, () => {
childWindow.document.querySelector("form[action='child2.html']").submit();
});
// do something with DOM
});
</script>
</head>
<body>
<h1>parent.html</h1>
</body>
</html>
If it is impossible, do you know any workarounds to achieve the goal?
No need for workarounds since it is possible.

Categories

Resources