I have something very strange happening in my program. I have consol.logged the crap out of it with timestamps to try and figure out what is happening. Basically my program randomly stops fetching data. I have ensured a new stream of data is there but I have to refresh the entire page or resave the program to restart everything when it gets hung up. On top of that, there are no errors or flags telling me why it stops. I tried to isolate the issue but it is something to do with the async function most likely. Here is the code....
function App() {
const data = async() => {
try {
console.log('try block initiated')
const apiResponse = await fetch(ipAddress)
console.log(apiResponse);
console.log("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB")
const responseText = await apiResponse.text();
console.log(responseText)
console.log("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC")
if (loading === true){
setLoading(false);
}
console.log("DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD")
return responseText;
} catch (error) {
console.log('catch initiated')
setError(true);
}
};
console.log("AAAAAAAAAAAAAAAAAAAAAAA")
try{
console.log("EEEEEEEEEEEEEEEEEEEEEEEEEEE")
data().then(response=>setMoisture(response));
console.log("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
} catch(error){
console.log('gotcha')
setError(true);
}
let value = moisture;
console.log(value);
count += 1;
console.log(count);
return(
<div className="App">
<WeatherCard moisture={value}/>
</div>
);
}
Here is what the console looks like right before it stop fetching new data...
HERE IS A POSSIBLE SOLUTION. After many headaches and researching async functions. I think this is going to work. I am going to let this run all night and see if it ever gets caught up but so far, Here is the solution...
const data = async() => {
try {
const apiResponse = await fetch(ipAddress)
const responseText = await apiResponse.text();
console.log("CCCCCCCCCCCCCCCCCCC");
console.log(responseText);
if (loading === true){
setLoading(false);
}
data().then(response=>setMoisture(response));
return responseText;
} catch (error) {
console.log("EEEEERRRRRRRRRROOOOOOOOOOORRRRRRRRRRRR")
setError(true);
}
};
console.log("AAAAAAAAAAAAAAAAAAAA");
if (moisture === "initialState"){
data().then(response=>setMoisture(response));
}else{
console.log("QQQQQQQQQQQQQQQQQQQQQQQQ")
}
Here is what I changed... I made an initial state called "initialSate and if the variable was this value, it runs the async function. I then NEVER CALL THIS ASYNC AGAIN OUTSIDE OF THE ASYNS ITSELF. Instead I call itself right before returning a value from the function. This works because my program is running while the async function is running. However, the program is sequential to itself and the async is sequential to itself. They are NOT sequential together. So, the async function now only gets called again once the async function finishes its first run. Simultaneaously the rest of my program is running. Before I think it was getting caught because my program was trying to call the async function before it had finished the first time. This is a pure guess because I have no way to proving that but it makes sense. If this overnight run fails. I will pull this from the solution. Otherwise, I think this will work. FINGERS CROSSED!
Related
Ok,
So I am using the puppeteer framework and I have an async function that interact with a webpage.
This function clicks and selects and elements of a webpage while it waiting for the traffic of the page to be idle.
This function works most of the time, but sometimes it stalls.
I want to be able to set a timeout so that if the function is taking longer than a certain amount of time, it throws an error and I can run it again.
So far I cannot seem to get this to work because I cannot get the callback function I pass to setTimeOut() to 'interact' with the outer function.
My code looks like this:
const scrap_webtite = async page => {
/* scrap the site */
try{ // catch all
// set timeout
let timed_out_ID = setTimeout(()=> throw "timeOut", 1000);
// run the async
let el = await sometimes_stalls_function(page);
// if function ran finished correcly
clearTimeout(timed_out_ID);
// save el
save_el(el);
}
}catch(e){
console.error("Something went wrong!", e);
// this makes the function run again
// here is where I want to ideally catch the timeout error
return false
}
}
I have also tried wrapping the setTimeOut function in an Promise as per this post and the using the .then().catch() callbacks to try to catch the error to no avail.
Apologies if this is a stupid question, thank for you help.
The problem you're running into is essentially that the error thrown in setTimeout() is not related to your function flow, and thus can't be caught there. You can essentially think of the timer's callback function as a "detached" function: the variables from the parent scope will still be available, but you can't return a value to the parent directly etc.
To work around this problem you have a few options, Promise.race() is one possible solution. The idea is to first make an async version of a timeout:
const rejectAfter = (timeout) => {
return new Promise((resolve, reject) => {
setTimeout(() => reject(), timeout);
});
};
Then extract your business logic out into a separate async function a-la:
const doTheThing = async () => {
// TODO: Implement
};
And finally in your scraping function, use Promise.race() to use the result from whichever of the two finishes first:
const scrape = async (page) => {
try {
const el = await Promise.race([
rejectAfter(1000),
doTheThing()
]);
} catch(error) {
// TODO: Handle error
}
}
try turning everything in the try block into a promise
const scrap_webtite = async page => {
/* scrap the site */
try{ // catch all
return await new Promise(async(r,j)=>{
// set timeout
let timed_out_ID = setTimeout(()=>j("timeOut"),1000);
// run the async
let el = await sometimes_stalls_function(page);
// if function ran finished correcly
clearTimeout(timed_out_ID);
// save el
r(save_el(el));
})
}catch(e){
console.error("Something went wrong!", e);
// this makes the function run again
// here is where I want to ideally catch the timeout error
return false
}
}
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.
I am currently working on a little JS game (node.js, socket.io) and I need to retrieve data from my MongoDB (currently using Mongoose). Getting the data itself seems not to be the problem since I can console log it.
The problem is the timing. I need the data before the programm continues running. I've tryed using async await, but I think I might still be using it wrong since It doesn't work.
card.controller.js:
exports.CRUD = {
getCardById : async (id) => {
try {
let cards = await Card.find({number:id}).exec();
return cards ;
} catch (err) {
return 'error occured';
}
}
}
game.js:
/* Code is within a normal function (not async) */
(async () => {
game.card = await CRUD.getCardById(cardId - 1);
console.log("Game-Card: ",game.card); // Data gets logged here, but only after it already returned game
})();
/*return game*/
I really struggle with async functions in Javasctipt. Here I have async function that calls api and saves the result. It is working fine but I need to make a loop now so this api is called untill certain condition is met. I understand that it needs to be done using await but I just can't figure out how exactly.
I tried setting up if statement and then doing something like "if condition is not met" setTimeout(getResults()); (repeat the call to async function).
async getResults() {
try {
const res = await axios(`https://blablabla`);
this.result = res.data.info;
} catch (error) {
alert(error);
}
}
async getResults() {
try {
let i = 100;
while(i-->=0){
const res = await axios(`https://blablabla`);
this.result = res.data.info;
if(this.result == 'some process finished')
{
return this.result;
}
//else retry
}
} catch (error) {
alert(error);
}
just use some cycle like while(true). And repeat body of cycle until your conditions are met
Ok, so I've just spent a day figuring out how to use callback-functions within sagas. (Please be nice, I'm just learning this saga-stuff)
My initial problem:
I get an xml-response from the server and before going into my reducer I want to parse it into a js-object. Therefore I use xml2js.
Calling this xml2js library works with a callback:
parseString(xmlInput, (err, jsResult) => {
// here I'd like to put() my success-event, however that's not possible in this scope
})
After reading a lot about eventChannels, I've come up with this solution:
My Channel-Function:
function parseXMLAsyncronously (input) {
return eventChannel(emitter => {
parseString(input, (err, result) => {
emitter(result)
emitter(END)
})
return () => {
emitter(END)
}
})
}
Using it inside the saga:
const parsedJSObject = yield call(parseXMLAsyncronously, xmlFromServer)
const result = yield take(parsedJSObject)
The problem that I'm encountering now is that apparently even while using a callback-structure, the parseString-function is still executed synchronously. So when I get to my yield-line, the parsing has already been done and I can wait forever because nothing will happen anymore.
What's working is to make the parsing asynchronously, by replacing
parseString(input, (err, result) => {...}
with
const parser = new Parser({async: true})
parser.parseString(input, (err, result) => {...}
So basically I'm making an already blocking function unblocking just to block (yield) it again and then wait for it to finish.
My question is now pretty simple: Is there maybe a smarter way?
Why not just use the cps effect?
try {
const result = yield cps(parseString, input)
} catch (err) {
// deal with error
}