ensure code is running twice with condition - javascript

I have code which needs to do request to some server with token,
Sometimes you need to run the code twice to get the data. (the exact same code )
How can I ensure that the code will run only twice and not recursively ?
Most of the time in the first run I got http response 401 and the second run give http 200
This is the code
async function magt() {
let auth: any;
try {
auth = await getTokens();
if (auth.runtime?.status == 200 && auth.application?.status == 200) {
...
//run rest of the code
} else {
console.log("unable to fetch tokens")
if (auth.response.status == 401) {
console.log("Running for the second time to get valid token")
await magt()
}
}
} catch (e) {
console.log("error occurred while fetching token: ", e)
}
I want to make sure that if I got always 401, run the magt() func only twice, what is the best way to do it ?
is there a better way from some simple counter which is defined by global?

How about a simple for loop?
async function magt() {
for (var attempt = 0; attempt < 2; attempt++) {
try {
let auth = await getTokens();
if (auth.runtime?.status == 200 && auth.application?.status == 200) {
return true;
}
console.log("Invalid auth response:", auth.response);
} catch (e) {
console.log("error occurred while fetching token: ", e);
}
}
throw new Error("All attempts at magt'ing failed miserably");
}
async function something() {
await magt();
//"run rest of the code"
}
Or, if you're feeling fancier, write a wrapper function for retrying async functions:
async function retrying(func, attempts) {
for (let attempt = 0; attempt < attempts; attempt++) {
try {
return await func();
} catch (err) {
console.log(func, "attempt", attempt, "failed:", err);
}
}
throw new Error("retry failed");
}
async function checkToken() {
const auth = await getTokens();
if (auth.runtime?.status == 200 && auth.application?.status == 200) {
return auth;
}
throw new Error("invalid auth response: " + auth.response);
}
async function magt() {
const token = await retrying(checkToken, 2);
}

async function magt(iterationLeft) {
let auth: any;
iterationLeft--;
try {
auth = await getTokens();
if (auth.runtime?.status == 200 && auth.application?.status == 200) {
...
//run rest of the code
} else {
console.log("unable to fetch tokens")
if (auth.response.status == 401 && iterationLeft) {
console.log("Running for the second time to get valid token")
await magt(iterationLeft)
}
}
} catch (e) {
console.log("error occurred while fetching token: ", e)
}
Calling with number of iteration as argument magt(2);

Related

catching exception thrown by service worker message event

I can't catch an exception thrown by the service worker's message event..
The client uses following code to execute the command on the SW:
import { messageSW } from "workbox-window";
// .. code for Workbox initialization/registration omitted
messageSW(registration?.active, { type: "SYNC" })
.then((results) => {
console.log("done");
})
.catch((e) => {
console.error(e);
});
On the SW (sw.js) side I have the following code:
self.addEventListener("message", async (event) => {
if (requestType === "SYNC") {
event.ports[0].postMessage(await longRunningTask());
}
});
This solution works OK as long as the SW is not throwing any exceptions. Meaning that the client prints the "done" message after the long running process on the SW is executed. If the exception is thrown nothing gets returned, ever.
I have managed to fix the problem by doing the following:
self.addEventListener("message", async (event) => {
if (requestType === "SYNC") {
try {
event.ports[0].postMessage(await longRunningTask());
} catch (error) {
event.ports[0].postMessage(error);
}
}
});
In this case - the result is always returned regardless, "done" is printed, but:
how do I actually produce an exception from the service worker, so the client could catch and handle it?
In general it would be good to hear if what I am doing is an appropriate approach to how asynchronous code on the SW shall be invoked from the client...
Here is my own solution I ended up using:
On service worker side - helper method:
async function replyToSenderAsync(event, task) {
let isCanReply = event.ports && event.ports.length >= 0;
try {
const result = await task();
if (isCanReply) {
event.ports[0].postMessage({ error: null, message: result });
}
} catch (error) {
if (isCanReply) {
event.ports[0].postMessage({ error: error, message: null });
}
}
}
When exception is caught we set the error property. Use as:
self.addEventListener("message", async (event) => {
const requestType = event?.data?.type;
if (requestType === "QUEUE_CLEAR") {
await replyToSenderAsync(event, async () => await clearQueueAsync());
}
});
On client side request wrapper:
function sendMessageToSWAsync(targetSW, messageType, message) {
return new Promise(function (resolve, reject) {
if (
!isServiceWorkerSupported.value ||
!isServiceWorkerRegistered.value ||
!targetSW
) {
reject(new Error("Unable to send the message to a service worker"));
}
try {
messageSW(targetSW, { type: messageType, message: message })
.then((messageResponse) => {
if (!messageResponse) {
reject(new Error("Service worker responsed with empty response"));
} else {
if (messageResponse.error) {
reject(messageResponse.error);
} else {
resolve(messageResponse.message);
}
}
})
.catch((messageError) => {
reject(messageError);
});
} catch (error) {
reject(error);
}
});
}
The magic here is to read the error property and reject the promise if that is the case (hence causing an exception to be thrown). Use as
try {
let response = await sendMessageToSWAsync(registration?.active, "QUEUE_GET_ALL");
}
catch(error) {
}
sendMessageToSWAsync(registration?.active, "QUEUE_GET_ALL")
.then((response) => {})
.catch((error) => {})

async function is not waiting for the end

My goal is, if the first request is catching an error, i dont want it to make another request. Basically conditional request.
Action.js
let condition;
Service.validation(payload).then(() => {
condition = true;
}).catch(errorMessage => {
console.log("1")
condition = false;
})
if (!condition) {
console.log("NOT valid.. closing..")
return;
} else {
console.log("VALID.. Calling another service")
const promise = AnotherService.anotherRequest(url, payload);
return promise.then().catch();
}
Service.js
async validation(payload) {
return this.createOrUpdate(payload, "check");
}
Generic class for crud
async createOrUpdate(data, subPath = "") {
try {
if (data.id) {
const response = await this.rc().put(this.PATH + subPath, data);
return response.data;
}
const response = await this.rc().post(this.PATH + subPath, data);
return response.data;
} catch (error) {
throw this.handleError(error);
}
}
rc = (responseType = "json") => {
return axios.create({
baseURL: this.RS_REST_URL,
validateStatus: (status) => (status === 200 || status === 201),
headers: {
"Content-Type": CONTENT_TYPE_JSON,
"Authorization": "Bearer " + localStorage.getItem(STORAGE_KEY_AT)
},
responseType: responseType,
paramsSerializer: (params) => qs.stringify(params, {arrayFormat: "repeat"})
});
}
I make a request const response = await this.rc().post gets an error (in Generic class) and thats i want. But, after i catch an error;
if (!condition) {
console.log("NOT valid.. closing..")
This part does not wait for service to set condition = false
Service.validation(payload).then(() => {
condition = true;
})**.catch(errorMessage => {
condition = false;
})**
it is an async function but code does not wait for it to finish.
IN THE CONSOLE
"NOT valid.. closing.."
"1"
Am i missing something here?
In Action. You need call await.
async function () => {
let condition;
await Service.validation(payload).....
if (!condition) ...
}

How to handle Promise that returns a 404 status?

I have a method that uses node-fetch to make a POST call to update a profile object in a table via an API. If an invalid profileId is provided (status 404) the promise still resolves. What's the best way to handle it so that I can only accept status 200? The method is defined as:
async function updateUserProfileSocketId(profileId, socketId) {
const body = { id: profileId, socketId };
try {
const response = await fetch(`${API_URL}/updateUserProfile`, {
method: 'post',
body: JSON.stringify(body),
headers: { 'Content-Type': 'application/json' },
});
if (response.status !== 200) {
throw new Error(response.status);
}
} catch (err) {
console.log(`updateUserProfileSocketId Error: ${err}`);
}
}
And the method is called in a service class like this:
onInit(socket) {
socket.on('init', (profile) => {
Promise.resolve(updateUserProfileSocketId(profile.id, socket.id))
.then((response) => {
if (response === null || response === undefined) {
console.log(`Unable to find profile ${profile.id}`);
socket.conn.close();
} else {
users.push(profile.id);
}
})
.catch((err) => {
console.log(err);
});
});
}
This seems to work, but I'm not sure if this is the best way to handle this. Any ideas?
If the response status is not 200, you throw an exception that will immediately be caught again. This is probably not what you want. You can leave the catch block for logging purposes, but you should rethrow the exception:
async function updateUserProfileSocketId(profileId, socketId) {
const body = { id: profileId, socketId };
try {
const response = await fetch(...);
if (response.status !== 200) {
throw new Error(response.status);
}
} catch (err) {
console.log(`updateUserProfileSocketId Error: ${err}`);
throw err;
}
}
The same thing applies to the catch-handler inside the socket-callback.
However, removing the try/catch/log/rethrow logic and handling the exception centrally would be cleaner.

Chaining multiple request using bluebird

I'm trying to convert my existing code using BlueBird, please suggest a best option to chain multiple request. Error happening in each callback needs to be redirected to rendered with different error.
request(option1, function (error, response, body) {
if (!error && response.statusCode == 200) {
var data= JSON.parse(body);
if(data.valid){
if(data.expired){
next();
} else {
request(option2, function (error2, response2, body2) {
var data2= JSON.parse(body2);
if(data2.valid) {
request(option3, function (error3, response3, body3) {
next();
})
} else {
res.json({error:'Error1'});
}
})
}
} else {
res.json({error:'Error2'});
}
} else {
res.json({error:'Error3'});
}
})
This is pretty straightforward, also note your current code doesn't handle errors in the second and third requests and this does:
var request = require("request-promise"); // request - converted to bluebird
request(option1).then(data=> {
if(!data.valid) throw Error("Error3");
if(data.expired) return;
return request(option2).then(JSON.parse);
}).then(data2 => {
if(!data2) return; // didn't need to fetch additional data
if(!data2.valid) throw Error("Error2");
return request(option3);
}).then(() => {
next();
}, e => {
res.json(error: e.message);
// better log this.
});
var rp = require('request-promise');
function handleError(err) {
res.json({
error: err.message
});
}
function parse(data) {
if (data) {
return JSON.parse(data);
}
}
rp(option1)
.then(parse)
.then(function (data) {
if (!data || !data.valid) {
throw Error('Error2');
}
if (data.expired) {
return;
}
return option2;
})
.then(rp)
.then(parse)
.then(function (data2) {
if (!data2 || !data2.valid) {
throw Error('Error1');
}
return option3;
})
.then(rp)
.then(parse)
.then(function () {
next();
})
.catch(handleError);
You don't need to manually check for statusCode but if you need to do so, first you have to add resolveWithFullResponse attribute to your option1 object, which allows you to receive the response object:
function checkStatusCode(response) {
if (response.statusCode !== 200) {
throw Error('Error3');
}
return response.body;
}
// add resolveWithFullResponse attribute to option1
option1.resolveWithFullResponse = true;
rp(option1)
.then(checkStatusCode)
.then(parse)
//...

Node JS: Request Loop Until Status Code 200

I currently have working code that does a request and checks if it receives a successful status code of 200. I would like to grow on this and loop it where it will keep sending requests until the status code is 200. I tried using a while loop but was not receiving the correct results. Thanks for the help!
request('http://0.0.0.0:9200', function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log('success');
do(something);
}
else {
console.log('fail');
}
});
Would be something like:
let retry = (function() {
let count = 0;
return function(max, timeout, next) {
request('http://0.0.0.0:9200', function (error, response, body) {
if (error || response.statusCode !== 200) {
console.log('fail');
if (count++ < max) {
return setTimeout(function() {
retry(max, timeout, next);
}, timeout);
} else {
return next(new Error('max retries reached'));
}
}
console.log('success');
next(null, body);
});
}
})();
retry(20, 1000, function(err, body) {
do(something);
});
You can set a max number of retries and a timeout between retries. So that you do not introduce an infinite loop, and you do not deliver the final punch to an overloaded request target ^^
I wanted a little more intuitive answer including promises. I build on top of miggs answer within a try/catch the code below with promises and axios.
Based on a simple example of recursive functions
const throwNumbers = (count = 0) => {
console.log(count);
if (count++ < 10) {
throwNumbers(count);
} else {
console.log('max reached');
};
};
You can put anything else on the try part and handle error codes in the catch part. You have to set a max number of retries, which is 10 in my case.
let getResponse = async(count = 0) => {
try {
const axiosResponse = await axios.get(someURL, {
params: {
parameter1: parameter1,
},
});
return axiosResponse;
} catch (error) {
if (error || error.status != 200) {
console.error('failed, retry');
if (count++ < 10) {
return getResponse(count);
} else {
throw new Error('max retries reached');
};
} else {
throw error;
};
};
};
You would call the function with the following and handle the body or whatever with the response value.
let response = await getResponse();
console.log('This is the response:', response);
Has no timeout but works for me.

Categories

Resources