Using async function to stockfish postMessage - javascript

I tried working with stockfish.js and I have this one problem. I also used #ninjapixel/chess for easier methods than the original.
(async function() {
console.log('test1`');
console.log(chess.ascii());
await stockfish.postMessage('go depth 15');
console.log(chess.ascii()); // this should change, but it doesn't
console.log('test2');
})();
Why does the async function with await keyword didn't work?
Why did the test2 was logged first before the test3 since I'm using an async function, it should work, but it didn't, why is that?
Here's how I handled onmessage:
stockfish.onmessage = (e) => {
let info = e.data ? e.data : e;
if (info.includes('bestmove') || info.includes('ponder')) {
const bestmove = info.split(/ +/)[1];
const ponder = info.split(/ +/)[3];
chess.move(bestmove, {
promotion: 'q',
sloppy: true
});
// test2 was logged first before this
console.log('test3'); // ascii worked in this part
stockfish.postMessage(`position fen ${chess.fen()}`); // update fen of stockfish
}
};
If there's no way to solve this then should I actually depend on the onmessage respond? Because what I wanna do is after I do postMessage I want to actually respond after the postMessage NOT directly inside the onmessage response.
Like something this:
let bestMove = null;
stockfish.onmessage = (e) => {
let info = e.data ? e.data : e;
if (info.includes('bestmove') || info.includes('ponder')) {
bestMove = info.split(/ +/)[1];
// set bestmove after calculating...
}
}
(async function() {
await stockfish.postMessage('go depth 15');
// do the move after the message
chess.move(bestMove);
await stockfish.postMessage(`position fen ${chess.fen()}`);
})();

The await keyword, didn't have any effect in your code because the function stockfish.postMessage doesn't return a Promise. postMessage emits an event that will be processed in the next tick. This is why console.log('test3') was logged after console.log('test2'). After emitting the event it will return synchronously, logging console.log('test2').
stockfish.onmessage will need to be the control centre of all events. It will need to understand the different messages, and do the appropiate actions. You dont need to put the implemention of the actions in the onmessage. You can create a function and called it from stockfish.onmessage... and then inside the action function you could call to stockfish.postMessage if you need to notify stockfis of a change... and so on.... and on...

Related

How to stop custom function in JavaScript

look at this code
const code = "..." // can be anything, it is unknown
const func = new Function(code)
// run `func` and ignore possible errors in code
function run() {
try { func() } finally {}
}
// stop `func` execution
function stop() {
???
}
$runButton.onclick = run
$stopButton.onclick = stop
Is there any way to stop function execution
I just need to rerun function multiple times, kind of playground
But if code has timers, or infinite loops it freezes behaves badly
I don't want to reload playground site every time
Any ideas?
Same Situation in TS Playground, they don't care (press run multiple times)
Tried this, but that does not make sense
If that function's body is unpredictable
Also another way, which seems to be the most inefficient
Put checks in every line of function's body, and if flag is false break main label which is declared at the top of function's body for 'return' effect
let isRunning = true
code.split('/n').map(line => ';' + `if (!isRunning) break functionLabel` + ';' + line)
const functionBody = `functionLabel: { ${code} }`
const func = new Function(functionBody)
Create a web worker and pass code as message,
theoretically, it should run it in the background not affecting the main thread. Then terminate worker if you need to stop execution
For multiple 'reruns' reinit worker
worker.js
self.addEventListener('message', event => {
const { code } = event.data
const func = new Function(code)
try {
func()
self.postMessage('code ran successfully')
} catch (error) {
// handle possible error
self.postMessage('code ran with errors')
}
})
main.js
let worker = null
function run() {
let code = '...'
worker?.postMessage({ code })
}
function stop() {
if (worker == null) {
worker = new Worker(pathToWorker)
worker.addEventListener('message', event => {
console.log(event.data)
})
} else {
worker.terminate()
worker = null
}
}
Web Workers Support (98%)

Code not continuing after JavaScript async function

I have a button on a local webapp that calls an async function to make another program go and do stuff via a websocket.
<button onclick="makeOtherProgramDoThing('arg1','arg2')">
The async function looks like:
async function makeOtherProgramDoThing(arg1,arg2){
let messageCode = sendMessage("someMessageData"); //This sends the message, see info below
let finalResponse = await receiveFinalResponse(10000,messageCode);
if(finalResponse[2]=='0'){ // no error
console.log('no error:'+finalResponse[3]); //finalResponse[3] is the errorcode
}else{
console.log('error:'+finalResponse[3]);
}
}
// Now I update the HMTL to show that it was successful...
}
The idea is that sendMessage() sends off the data to my other program, which then replies to another .js file that has a variable called lastReceivedMessage. Back in the async function, after I send off the message I await for recieveFinalResponse(), which looks like this.
function receiveFinalResponse(msTimeOut,messageCode){
return new Promise((resolve,reject) =>{
var startTime= performance.now();
while(true){
if (performance.now()-startTime > msTimeOut){
reject('Timed Out');
break;
}
if(typeof lastReceivedMessage!== "undefined"){
if(lastReceivedMessage[1]==='messageCode'){
resolve(lastReceivedMessage[0]+";"+lastReceivedMessage[1]+";"+lastReceivedMessage[2]+";"+lastReceivedMessage[3]);
break;
}
}
}
})
}
So, I'm expecting receiveFinalResponse() to keep looping and checking the other js file for the lastMessageReceived that matches the messageCode, within a timeout timeframe.
But what actually happens is: sendMessage() sends the message, then receiveFinalMessage() starts looping and every loop the lastMessageReceived is unidentified, until it times out and moves past the await, at which point the other js file finally updates lastMessageReceived. I thought that the code would hit the async function, start running it, and then continue doing other things in the background, but it seems to just remain synchronous and hit then function, step though it normally, and only reach the other code once its finished. Is it because I'm using a while loop to wait for the response? How else could I wait for a response and freeze one function until I get the response while letting other code run in the background?
Thanks in advance to anyone that helps.
If I were to reproduce/mockup your code to a working example it would be as below, and it does indeed reproduce your problem.
const delay = ms => new Promise(resolve => setTimeout(resolve,ms));
async function sendMessage(msg){
await delay(1000);
window.lastReceivedMessage = ["foo", "messageCode",123456,"abc","def"];
}
function receiveFinalResponse(msTimeOut,messageCode){
return new Promise((resolve,reject) =>{
var startTime= performance.now();
while(true){
if (performance.now()-startTime > msTimeOut){
reject('Timed Out');
break;
}
if(typeof lastReceivedMessage!== "undefined"){
if(lastReceivedMessage[1]==='messageCode'){
resolve(lastReceivedMessage[0]+";"+lastReceivedMessage[1]+";"+lastReceivedMessage[2]+";"+lastReceivedMessage[3]);
break;
}
}
}
})
}
(async function(){
let messageCode = sendMessage("someMessageData"); //This sends the message, see info below
try{
let finalResponse = await receiveFinalResponse(10000,messageCode);
console.log(finalResponse);
}catch(e){
console.log(e);
}
})()
Note that I used a delay method there, in order to reproduce the sendMessage function doing some work. It should have done that work in 1 second, yet the timeout was set at 10 seconds and it still timed out. This is because your while loop keeps hold of the thread and never yields to allow the work to finish - remember javascript is single threaded.
In order to fix this issue, you need to use that same delay concept within your while loop to yield control elsewhere for work to be done.:
const delay = ms => new Promise(resolve => setTimeout(resolve,ms));
async function sendMessage(msg){
await delay(1000);
window.lastReceivedMessage = ["foo", "messageCode",123456,"abc","def"];
}
async function receiveFinalResponse(msTimeOut,messageCode){
var startTime= performance.now();
while(true){
if (performance.now()-startTime > msTimeOut){
throw('Timed Out');
break;
}
if(typeof lastReceivedMessage!== "undefined"){
if(lastReceivedMessage[1]==='messageCode'){
return lastReceivedMessage[0]+";"+lastReceivedMessage[1]+";"+lastReceivedMessage[2]+";"+lastReceivedMessage[3];
break;
}
}
await delay(500);
}
}
(async function(){
let messageCode = sendMessage("someMessageData"); //This sends the message, see info below
try{
let finalResponse = await receiveFinalResponse(10000,messageCode);
console.log("fr",finalResponse);
}catch(e){
console.log("ex",e);
}
})()
Sidenote: did you mean for if(lastReceivedMessage[1]==='messageCode'){ to actually be if(lastReceivedMessage[1]===messageCode){? Otherwise the second argument to your function is somewhat redundant.

Why is video not displaying when user joins room?

So, I have an adaptation of https://github.com/Dirvann/mediasoup-sfu-webrtc-video-rooms working in vanilla JS that I am attempting to adapt to use React. Instead of every user being a broadcaster, in my version, only the room creator is the broadcaster.
I've hit an issue. In the React version, when a viewer navigates to the room, they are not receiving the stream! I have no idea why since they use the same RoomClient class: https://github.com/Dirvann/mediasoup-sfu-webrtc-video-rooms/blob/master/public/RoomClient.js
This line const consumer = await this.consumerTransport.consume({ id, producerId, kind, rtpParameters, codecOptions, }); seems to be causing the problem, since the log following it doesn't get printed. Inside the consume function, my log that says 'hi' is executed, but 'blah' is not.
Here is a screenshot of the client console:
The most important functions are found below. For the entire class, please click the github link above.
async consume(producer_id) {
//let info = await roomInfo()
console.log('consume ', producer_id);
console.log('dddddddddddd', await this.getConsumeStream(producer_id));
this.getConsumeStream(producer_id).then(
function ({ consumer, stream, kind }) {
console.log('blah');
this.consumers.set(consumer.id, consumer);
let elem;
console.log('clg kind === ', kind);
if (kind === 'video') {
console.log('cons vid');
elem = document.createElement('video');
elem.srcObject = stream;
elem.id = consumer.id;
elem.playsinline = false;
elem.autoplay = true;
elem.className = 'vid';
this.remoteVideoEl.appendChild(elem);
} else {
elem = document.createElement('audio');
elem.srcObject = stream;
elem.id = consumer.id;
elem.playsinline = false;
elem.autoplay = true;
this.remoteAudioEl.appendChild(elem);
}
consumer.on(
'trackended',
function () {
this.removeConsumer(consumer.id);
}.bind(this)
);
consumer.on(
'transportclose',
function () {
this.removeConsumer(consumer.id);
}.bind(this)
);
}.bind(this)
);
}
async getConsumeStream(producerId) {
const { rtpCapabilities } = this.device;
console.log('rtpcaps ', rtpCapabilities);
const data = await this.socketRequest('consume', {
rtpCapabilities,
consumerTransportId: this.consumerTransport.id, // might be
producerId,
}).then((data) => {
console.log('daaatttaaa', data);
return data;
});
const { id, kind, rtpParameters } = data;
console.log('data === ', data);
let codecOptions = {};
console.log('aaaaaaaaaaaaaa', this.consumerTransport.consume);
const consumer = await this.consumerTransport
.consume({
id,
producerId,
kind,
rtpParameters,
codecOptions,
})
.then((result) => {
console.log('bbbbbbb', result);
return result;
});
console.log('consumer === ', consumer);
const stream = new MediaStream();
console.log('stream === ', stream);
stream.addTrack(consumer.track);
console.log('kind ', kind);
return {
consumer,
stream,
kind,
};
}
Many thanks for your time
---update---
This line never resolves: const consumer = await this.consumerTransport.consume({ id, producerId, kind, rtpParameters, codecOptions, })
It ends up executing some complex-looking functions from packages. In fact, It seems to get stuck in an infinite loop in the package called sdp-transform, after executing a few lines in mediasoup-client.
I don't even understand how chrome debugger's working. Because if I put a breakpoint on that line where ...consume(... is called, and click 'step', it parses this line: let answer = await this._pc.createAnswer() ... Which is part of a function called receive, which wasn't called by the breakpoint line... Please, somebody help.
You said something interesting there. 'hi' gets logged and 'blah' not. There isn't much code in between them. This should make us wonder how it is even possible.
Maybe create the same just without any distractions around.
Promise.resolve()
.then(console.log('hi'))
.then(function() {
console.log('blah');
})
What would that do? and why?
.then is a function that expects to be given a function (to be called when the promise resolves...)
What does console.log('hi') return? And when is it executed?
You did not want to execute the log when creating the promise chain and you also want to maybe return the result again since you want to work with the value.
So my guess would be. Changing it to:
.then(result => {
console.log('hi')
return result
})
brings you at least a step closer.

How to let a webworker do multiple tasks simultaneously?

I am trying to let a Web-Worker manage its state, meanwhile serving multiple async requests.
worker.ts file
let a =0; //this is my worker's state
let worker=self as unknown as Worker;
worker.onmessage =(e)=>{
console.log("Rec msg", e.data);
if(e.data === "+1"){
setTimeout(()=>{
a=a+1;
worker.postMessage(a);
},3000);
}else if(e.data=== "+2"){
setTimeout(()=>{
a=a+2;
worker.postMessage(a);
},1000)
}
}
And this is my main file: main.ts
let w =new Worker("./worker.ts", {type: "module"})
let wf =async (op: string)=>{
w.postMessage(op);
return new Promise<any>((res,rej)=>{
w.onmessage=res;
});
}
(async()=>{
let f1 = await wf("+1");
console.log("f1",f1.data);
})();
(async()=>{
let f2 = await wf("+2");
console.log("f2",f2.data);
})()
Only f2 is returned , and f1 is lost.
I have used timeouts to simulate say some async task done by worker themselves.
How do I receive both f1 and f2?
Your problem is that you are trying to take an event based API and use it as a Promise based one, but events may fire multiple times, while Promise should resolve only once.
The communication between the Worker and the main thread works by sending and receiving messages, but there is by default no one-to-one relation between these messages. Both ends of the communication (ports) will simply stack incoming messages, and handle them sequentially, when they'll get time.
In your code, the main thread's worker.onmessage handler of f1 has been overwritten by the second call f2 synchronously (one microtask later, but that's still synchronous for our matter).
You could attach your event using the addEventListener method, at least this way it wouldn't be overwritten. But even then, when the first message event will fire on worker, both handlers will think it's there own message that did arrive, while in fact it was the one of f2. so that's not what you need...
What you need is to set up a protocol of communication which would allow both ends to identify each task. You could for instance wrap all your tasks' data with an object containing a .UIID member, be sure both ends wraps their message this way, and then from main thread check that UUID to resolve the appropriate Promise.
But that can become a bit complicated to implement and to use.
My personal favorite way is to create a new MessageChannel per task. If you don't know this API, I invite you to read this answer of mine explaining the basics.
Since we are sure the only one message that will come through this MessageChannel is the response from the Worker to the one task we sent to it, we can await it just like a Promise.
All we have to do, is to make sure that in the Worker thread we respond through the transferred port instead of the global scope.
const url = getWorkerURL();
const worker = new Worker(url)
const workerFunc = (op) => {
// we create a new MessageChannel
const channel = new MessageChannel();
// we transfer one of its ports to the Worker thread
worker.postMessage(op, [channel.port1]);
return new Promise((res,rej) => {
// we listen for a message from the remaining port of our MessageChannel
channel.port2.onmessage = (evt) => res(evt.data);
});
}
(async () => {
const f1 = await workerFunc("+1");
console.log("f1", f1);
})();
(async () => {
const f2 = await workerFunc("+2");
console.log("f2", f2);
})()
// SO only
function getWorkerURL() {
const elem = document.querySelector( '[type="worker-script"]' );
const script = elem.textContent;
const blob = new Blob( [script], { type: "text/javascript" } );
return URL.createObjectURL( blob );
}
<script type="worker-script">
let a = 0;
const worker = self;
worker.onmessage = (evt) => {
const port = evt.ports[0]; // this is where we will respond
if (evt.data === "+1") {
setTimeout(() => {
a = a + 1;
// we respond through the 'port'
port.postMessage(a);
}, 3000);
}
else if (evt.data === "+2") {
setTimeout(() => {
a = a + 2;
// we respond through the 'port'
port.postMessage(a);
}, 1000)
}
};
</script>

Race condition in Amplify.Hub signIn handler when using oath

Example code:
Hub.listen('auth', event => {
const { event: type, data } = event.payload;
if (type === 'signIn') {
const session = data.signInUserSession;
console.log('SESSION', data.signInUserSession);
setTimeout(() => {
console.log('SESSION', data.signInUserSession);
}, 100);
}
});
When using oath, after the provider redirects to my app, the Hub fires a signIn event. However, the signInUserSession property is null when the event is fired, but gets a value some time later (within 100 ms). This does not seem to occur when using Auth.signIn(email, password) directly; signInUserSession is populated when the event is fired.
What is happening here, and how can I get around it? Currently, I have an explicit delay in the code, which is a terrible hack.
Perhaps the old way of JavaScript for waiting for value to be populated is useful to ensure that code does not fail even if the it takes longer than expected in populating the value.
Here is a sample code that I normally use when no other options are available.
waitForValue(){
if(myVar!= null && typeof myVar !== "undefined"){
//value exists, do what you want
console.log(myVar)
}
else{
setTimeout(() => {this.waitForValue()}, 100);
}
}
You can refactor this sample code as per your need.
Alternatively, AWS Amplify also have other ways to get current logged in user session. e.g. Auth.currentAuthenticatedUser() and Auth.currentSession() return promise. They can be used like this
private async getUser(){
let user = null;
try {
user = await Auth.currentAuthenticatedUser();
//console.log(user);
} catch (err) {
//console.log(err);
}
//return user;
}
i am not used to aws amplify - just read some github and so far i can see we will need info about your userPool implementation - i guess some weird callback issue
But for a workaround you can proxy the reference:
const event = {type: "signIn", data: {signInProperty: "null"}}
setTimeout(()=>event.data.signInProperty = "{Stack: Overflow}", 1000)
// mock events
function emit(type, args){
console.log(type, args)
}
//initialize
let watchedValue = event.data.signInProperty
document.getElementById("app").innerHTML = event.data.signInProperty
// protect reference
Object.defineProperty(event.data, "signInProperty", {
set(newValue){
watchedValue = newValue
document.getElementById("app").innerHTML = newValue
emit("event:signInCompleted", event.data)
},
get(){
return watchedValue
}
})
<div id="app"></div>

Categories

Resources