Reputation: 23
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.
Upvotes: 0
Views: 513
Reputation: 136094
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.
Upvotes: 1