Error handling when testing Solana event emitting - javascript

I'm writing a test of event emitting in my Solana program as described here: https://github.com/coral-xyz/anchor/blob/master/tests/events/tests/events.js
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.Events;
it("Is initialized!", async () => {
let listener = null;
let [event, slot] = await new Promise((resolve, _reject) => {
listener = program.addEventListener("MyEvent", (event, slot) => {
resolve([event, slot]);
});
program.rpc.initialize();
});
await program.removeEventListener(listener);
assert.isAbove(slot, 0);
assert.strictEqual(event.label, "hello");
});
It works good if the instruction completes successfully. But if any error happens during execution, the test code silently waits forever for event emitting which expectedly doesn't happen.
Can anyone please suggest a way to deal with such exceptions so that they are not "swallowed" and thrown on upper level?

If I understand the issue correctly, you need to add some sort of timeout when waiting for the event, correct? In that case, you should be able to use a setTimeout to check the result of your listener and error if it hasn't fired, ie:
it("Is initialized!", async () => {
let listener = null;
let event = {label: ""};
let slot = 0;
setTimeout(function(){
assert.isAbove(slot, 0);
assert.strictEqual(event.label, "hello");
},5000);
[event, slot] = await new Promise((resolve, _reject) => {
listener = program.addEventListener("MyEvent", (event, slot) => {
resolve([event, slot]);
});
program.rpc.initialize();
});
await program.removeEventListener(listener);
});

Related

How do I queue incoming websocket events in javascript for slow execution?

I have an open Websocket connection and it's handing out events. All good, but once a new event arrives, I need to do a whole lot of things and sometimes events arrive so quickly one after the other that there is no time to get the stuff done properly. I need some sort of queue inside this function that tells the events to take it easy and only keep going at most one per second, and otherwise wait in some sort of queue until the second elapses to go ahead and continue.
edit: No external libraries allowed, unfortunately.
ws = new WebSocket(`wss://hallo.com/ws/`);
ws.onmessage = readMessage;
async function readMessage(event) {
print(event)
//do important things
//but not too frequently!
}
How do I do that?
I found this but it goes over my simple head:
"You can have a queue-like promise that keeps on accumulating promises to make sure they run sequentially:
let cur = Promise.resolve();
function enqueue(f) {
cur = cur.then(f); }
function someAsyncWork() {
return new Promise(resolve => {
setTimeout(() => {
resolve('async work done');
}, 5);
}); } async function msg() {
const msg = await someAsyncWork();
console.log(msg); }
const main = async() => {
web3.eth.subscribe('pendingTransactions').on("data", function(tx) {
enqueue(async function() {
console.log('1st print: ',tx);
await msg();
console.log('2nd print: ',tx);
});
}) }
main();
"
I'd honestly use something like lodash's throttle to do this. The following snippet should solve your problem.
ws = new WebSocket(`wss://hallo.com/ws/`);
ws.onmessage = _.throttle(readMessage, 1000);
async function readMessage(event) {
print(event)
//do important things
//but not too frequently!
}
For achieving queuing, you can make use of "settimeout" in simple/core javascript.
Whenever you receive a message from websocket, put the message processing function in a settimeout, this will ensure that the message is processed not immediately as its received, but with a delay, hence in a way you can achieve queuing.
The problem with this is that it does not guarantee that the processing of messages is sequential as they are received if that is needed.
By default settimeout in javascript does give the guarantee of when the function inside will be triggered after the time given is elapsed.
Also it may not reduce the load on your message processor service for a high volume situation and since individual messages are queued two/more functions can become ready to be processed from setimeout within some time frame.
An ideal way to do so would be to create a queue. On a high level code flow this can be achieved as follows
var queue = [];
function getFromQueue() {
return queue.shift();
}
function insertQueue(msg) { //called whenever a new message arrives
queue.push(msg);
console.log("Queue state", queue);
}
// can be used if one does not want to wait for previous message processing to finish
// (function executorService(){
// setTimeout(async () => {
// const data = getFromQueue();
// await processData(data);
// executorService();
// }, 1000)
// })()
(function executorService(){
return new Promise((res, rej) => {
setTimeout(async () => {
const data = getFromQueue();
console.log("Started processing", data)
const resp = await processData(data); //waiting for async processing of message to finish
res(resp);
}, 2000)
}).then((data) =>{
console.log("Successfully processed event", data)
}).catch((err) => {
console.log(err)
}).finally(() => {
executorService();
})
})()
// to simulate async processing of messages
function processData(data){
return new Promise((res, rej) => {
setTimeout(async () => {
console.log("Finished processing", data)
res(data);
}, 4000)
})
}
// to simulate message received by web socket
var i = 0;
var insertRand = setInterval(function(){
insertQueue(i); // this must be called on when web socket message received
i+=1;
}, 1000)

speechSynthesis.speak() doesn`t output any sound

I am trying to create a website that scrapes a news website and reads it.
For some reason, whenever I am trying to read the actual info of the article, it reads part of it and stops after a few seconds.
important to point out :
I am using chromium.
The text I'm inserting doesn't reach speechSynthesis.speak()
text limit.
My function :
export async function textToSpeech(text) {
new Promise((resolve) => {
let msg = new SpeechSynthesisUtterance();
msg.voice = voices[6];
msg.lang = "en";
msg.text = text;
speechSynthesis.speak(msg);
msg.addEventListener("end", () => {
resolve();
});
});
}
calling the function :
ReadNews(data) {
textToSpeech(data.Title)
.then(textToSpeech(data.Info))
.then(textToSpeech("Would You like me to continue?"))
.then(
ContinueCON.addEventListener("click", () => {
textToSpeech(data.Content);
})
);
};
The first 3 instances (data.title, data.info, and the default message) are all spoken as expected. But on the 4th instance, stops after a few seconds.
Several things I have tried:
I used window.speechSynthesis.speaking right after the sound stopped working, and it printed true(which is very bizarre)
1st Edit (Yet to be solved)
Changed the code by the comments below
export function textToSpeech(text) {
return new Promise((resolve) => {
let msg = new SpeechSynthesisUtterance();
msg.voice = voices[6];
msg.lang = "en";
msg.text = text;
speechSynthesis.cancel(msg);
speechSynthesis.speak(msg);
msg.addEventListener("end", () => {
resolve();
});
});
}
Async was unnecessary and also "clean" the "Speak" queue (by using cancel)
ReadNews(data) {
textToSpeech(data.Title)
.then(() => textToSpeech(data.Info))
.then(() => textToSpeech("Would You like me to continue?"))
.then(() =>
ContinueCON.addEventListener("click", () => {
textToSpeech(data.Content);
})
);
},
};
I invoked the return value immediately instead of waiting for a resolve and then running the next line of text.
Problem is yet to be solved.
I see a couple issues with your code, which are causing the speech to all get queued up right away, without waiting. But speechSynthesis is supposed to handle that automatically, so it's not clear to me how they would cause what you're seeing. Still, i'll point them out in case they're causing your problem in a subtle way that i can't identify.
The first issue is that your textToSpeech function doesn't return the promise it creates. A promise is implicitly returned since it's an async function, but that promise will not wait for the speech to finish. The fix for this is to add a return in (and you can also remove async if you wish, since you're not awaiting anything)
export function textToSpeech(text) {
return new Promise((resolve) => {
let msg = new SpeechSynthesisUtterance();
msg.voice = voices[6];
msg.lang = "en";
msg.text = text;
speechSynthesis.speak(msg);
msg.addEventListener("end", () => {
resolve();
});
});
}
Secondly, ReadNews is not waiting for the promises to finish. .then(textToSpeech(data.Info)) means "immediately call textToSpeech, passing in data.Info, and then whatever it returns pass that into .then". Instead, you want to pass a function into .then, so that the function will be called once the previous promise has resolved:
ReadNews(data) {
textToSpeech(data.Title)
.then(() => textToSpeech(data.Info))
.then(() => textToSpeech("Would You like me to continue?"))
.then(
() => ContinueCON.addEventListener("click", () => {
textToSpeech(data.Content);
})
);
}
Or with async/await:
async ReadNews(data) {
await textToSpeech(data.Title);
await textToSpeech(data.Info);
await textToSpeech("Would you like me to continue?");
ContinueCON.addEventListener("click", () => {
textToSpeech(data.Content);
})
}

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>

Why does the frequency of my received websockets sometimes equal zero?

Im reciving websocket messages on my webpage, and i'm trying to calculate the frequency at which they are received. I do this like so:
let startTime = new Date();
ws.onmessage = function (evt)
{
prevData = recivedData;
var receivedMsg = evt.data;
recivedData = JSON.parse(receivedMsg);
const endTime = new Date();
const timeDif = endTime - startTime;
startTime = endTime;
console.log(timeDif)
}
When I take a look at the console to see what it has printed, I see that the frequency is mostly around 60 ms (as expected). Although, every fourth time or so, it will be 0 ms, I find this unlikely and I cant find what is causing it. Any ideas on how to fix this issue?
ws.onmessage = async () => {
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
await wait(50); // await between executions
// do your code
}
Update:
See this sample code below. You said that you have some loop function. Maybe it will be helpful you if you have access to the event queue every cycle and never have more than one event in the queue.
let done = false;
setTimeout(() => {
done = true
}, 5);
const eventLoopQueue = () => {
return new Promise(resolve =>
setImmediate(() => {
console.log('event loop');
resolve();
})
);
}
const run = async () => {
while (!done) {
console.log('loop');
await eventLoopQueue();
}
}
run().then(() => console.log('Done'));
What happens here. You call eventLoopQueue() function every cycle. This function pushes the callback to the queue by setImmidiate(). The callback is executed right away and anything that is in the queue are going to be executed as well. So every cycle you clear up the queue. And I believe it will help you.

nodejs: event.on is async?

I am reading this post: write async function with EventEmitter
Is the following piece of code async?
var event = new events.EventEmitter();
event.on("done", cb);
In the given code in question, you are subscribing for an event. When you call event.emit("done"), node execute the given callback in the same order they are subscribed.
Example
var event = new events.EventEmitter();
event.on("done",() => {
console.log(("notified 1"))
});
event.on("done",() => {
setImmediate(() => {
console.log("async")
});
console.log(("notified 2"))
});
event.on("done",async () => {
console.log(("notified 3"))
});
console.log("before firing an event");
event.emit("done");
console.log("after firing an event");
Output
before firing an event
notified 1
notified 2
notified 3
after firing an event
async

Categories

Resources