I have code that basically looks like this:
return Promise.all([
myPromise("foo", () => saveToFirebase("foo")),
myPromise("bar", () => saveToFirebase("bar")),
... // 50 more of these ^
]).then(() => {
res.status(200).send('ok');
}).catch(err => {
console.log(err.stack);
res.status(500).send('error');
});
My request times out because some of my requests take a long time. My question is: if I just called res.status(200) without chaining it Promise.all, would my unresolved promises still resolve even after a response is sent? Or would all execution of my code stop once a response is sent?
Like this:
Promise.all([
myPromise("foo", () => saveToFirebase("foo")),
myPromise("bar", () => saveToFirebase("bar")),
... // 50 more of these ^
])
res.status(200).send('ok');
The promise will always be executed. As you can see in following picture after response is send after promise resolve is printed on console.
And you can also keep the connection alive by setting res.shouldKeepAlive to true like this :
Promise.all([
check(2),
check(2),
check(2)
]).then(() => {
console.log('after promise resolve');
setTimeout(function() {
res.send('done');
}, 10000);
});
console.log('Before promise resolve');
res.shouldKeepAlive = true;
All promises will be either resolved or rejected. Express does not stop any execution when you finish request. You can try it out by simple console log:
Promise.all([
myPromise("foo", () => saveToFirebase("foo")),
myPromise("bar", () => saveToFirebase("bar")),
... // 50 more of these ^
])
.then(() => console.log("promises resolved"))
.catch(err => console.error("promises rejected", err))
res.status(200).send('ok')
However with this approach you don't know if anything went wrong when sending response.
Alternatively you could keep the connection alive like this:
const keepalive = setInterval(_=>res.write("."),1000);
return Promise.all([
myPromise("foo", () => saveToFirebase("foo")),
myPromise("bar", () => saveToFirebase("bar")),
... // 50 more of these ^
]).then(() => {
res.status(200).send('ok');
}).catch(err => {
console.log(err.stack);
res.status(500).send('error');
}).then(_=>clearInterval(keepalive));
Related
how can I make a second request if .catch on promise is executed , in order to get the data successfully
because if .catch is executed I did not received the data. and I have to refresh the page again to get the data.
fetch("https://randomuser.me/api")
.then(result => result.json())
.then(data => {
console.log(data)
})
.catch(error => console.log(error))
You just want to retry in the catch? You will need to call the function that makes the request again. This is called recursion. Ie:
function makeRequest() {
fetch("https://randomuser.me/api")
.then((result) => result.json())
.then((data) => {
console.log(data);
})
.catch((error) => {
console.log(error);
return makeRequest(); // Calls itself recursively
});
}
However this introduces the possibility of an infinite loop, so you need some way to break out, maybe like this:
function makeRequest(attempt=0) {
const maxRetries = 3;
if (attempt > maxRetries) {
throw new Error(`Could not fetch user after ${attempt} attempts`);
}
fetch("https://randomuser.me/api")
.then((result) => result.json())
.then((data) => {
console.log(data);
})
.catch((error) => {
console.log(error);
// Call self and increment the attempt number.
// Very important to ensure that the break condition
// can be met or we can end up calling the function
// forever with no way to escape. This is called an
// infinite loop.
return makeRequest(attempt + 1);
});
}
You can also make the logic more complex for retries, like introduce a timeout before the next request if attempt is gt 0, add exponential backoff etc.
Always remember to be careful of infinite loops when using recursive functions.
You can put the fetch and .json() calls into a function, then call that function once (immediately), and call it again inside the .catch if needed:
const getData = () => fetch("https://randomuser.me/api")
.then(result => result.json())
getData()
.then(console.log)
.catch((error) => {
console.log(error);
return getData();
});
.catch((error2) => {
// Could not get response after 2 attempts
console.log(error2);
});
Is it bad practice to nest multiple then functions? It seems fairly logical to say "execute this function, and when it's done, execute this one" (and so on) but the code looks horrible.
If it helps I originally had this query in the context of firestore getting user details then getting documents
firebaseApp.auth().signInWithEmailAndPassword(email, password).catch(function(error) {
//If error
}).then(()=>{
firebaseApp.firestore().collection(collectionName).where("associatedID", "==", authID).get().then((snapshot)=>{
snapshot.docs.forEach(doc => {
//Do stuff with data that we've just grabbed
})
}).then(()=>{
//Tell the user in the UI
});
});
Are there alternatives? One that springs to mind is like so
var functionOne = () =>{
console.log("I get called later");
}
var promise1 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 3000);
});
promise1.then(function(value) {
functionOne();
});
But even then it seems like it could get complex after a few .then()'s
Return the Promise from the first outer .then, and then use the resolve value in a second outer .then, without any nested .thens:
firebaseApp.auth().signInWithEmailAndPassword(email, password)
.then(()=>{
return firebaseApp.firestore().collection(collectionName).where("associatedID", "==", authID).get()
})
.then((snapshot) => {
snapshot.docs.forEach(doc => {
//Do stuff with data that we've just grabbed
});
//Tell the user in the UI
})
.catch((error) => {
// handle errors
});
Make sure not to catch too early - if there's an error anywhere in the chain, often you'll want to stop normal execution and go directly to the end (eg, tell the user that there was an error).
If you're worried about code readability, consider using async/await (and transpile down your production code for older browsers):
// in an async function:
try {
await firebaseApp.auth().signInWithEmailAndPassword(email, password);
const snapshot = await firebaseApp.firestore().collection(collectionName).where("associatedID", "==", authID).get()
snapshot.docs.forEach(doc => {
//Do stuff with data that we've just grabbed
});
//Tell the user in the UI
} catch(error) {
// handle errors
}
It depends on what you want to do: If you need access both to the result passed into then and to the result of a subsequent operation you're doing within the then at the same time, nesting is reasonable:
doSomething()
.then(result1 => {
return doSomethingElse()
.then(result2 => {
return result1 + result2;
});
})
.then(combinedResult => {
// Use `combinedResult`...
})
.catch(/*...*/);
often, though, you just need to pass a single value through the chain, by returning the promise from your subsequent operation from the then handler:
doSomething()
.then(result => {
return doSomethingElse(result);
})
.then(lastResult => {
// `lastResult` is the fulfillment value from `doSomethingElse(result)`
})
.catch(/*...*/);
Doing that resolves the promise then created to the promise returned by get() on the query. (To "resolve a promise to something" means that you've made the promise's settlement depend on the thing you've resolved it to. If you resolve it to another promise, its settlement depends on the settlement of that other promise.)
Looking at your Firebase example, I'd probably do it without nesting:
firebaseApp.auth()
.signInWithEmailAndPassword(email, password)
.then(() => firebaseApp.firestore().collection(collectionName).where("associatedID", "==", authID).get())
.then((snapshot) => {
snapshot.docs.forEach(doc => {
// Do stuff with data
});
})
.then(() => {
// Tell the user in the UI
})
.catch(function(error) {
// Handle/report error, which may be from `signInWithEmailAndPassword`, your collection query, or an error raised by your code in the `then` handlers above
});
You should chain promises and, also, you can name the functions, which IMHO can improve readibility significantly. Consider something like this
const signIn = () => firebaseApp.auth().signInWithEmailAndPassword(email, password);
const onSigninError = (err) => // error handling logic here
const getCollection = () => firebaseApp.firestore().collection(collectionName).where("associatedID", "==", authID)
.get();
const processSnapshot = (snapshot) => snapshot.doc.forEach(// do stuff here
const displayMessage = () => // do stuff here
signIn()
.catch(onSigninError)
.then(getCollection)
.then(processSnapshot)
.then(displayMessage);
I'm using an async function to start a server, check if the url returns 200, and then run my tests, but my checkUrl function doesn't complete before running the next line of code.
I'm using the axios package to ping the url for the status code and I've been trying several variations of async/await and Promise.resolve().
function checkUrl(url) {
console.log(`Checking for ${url}`);
return axios
.get(url)
.then(function(res) {
const message = `${url} is status code: ${res.status}`;
return res;
})
.catch(function(err) {
console.log("Will check again in 5 seconds");
setTimeout(() => {
return checkUrl(url);
}, 5000);
});
}
async function init() {
let val;
console.log("Running tests:::");
// start server in react-integration directory
shell.exec("BROWSER=none yarn start", {
cwd: "sampleIntegration/react-integration",
async: true
});
// check server has started
val = await checkUrl("http://localhost:3000");
console.log(`value from checkUrl promise: ${val}`);
}
I'm expecting the val variable to be the message returned from the Promise.resolve() in my checkUrl function.
val is coming back undefined.
The problem is, the setTimeout in the catch block. This is asynchronous, but the catch block immediately returns. As it does not have anything to return, it returns undefined. To resolve this (and also keep your desired behaviour of waiting) you could do something like this:
function checkUrl(url) {
console.log(`Checking for ${url}`);
return axios.get(url)
.then(res => {
const message = `${url} is status code: ${res.status}`;
return res;
})
.catch(err => {
console.log('Will check again in 5 seconds');
return new Promise((resolve) => {
setTimeout(resolve, 5000);
}).then(() => checkUrl(url));
});
}
This will make a Promise which resolves after 5 seconds and on resolve it calls checkUrl.
private runMiddlewares(route: Route | ExceptionRoute, request: HttpRequest, response: HttpResponse): Promise<any> {
return new Promise((resolve, reject) => {
try {
route.middlewares.forEach(async (middleware: IMiddleware) => {
console.log('begin run middleware');
await middleware.process(request, response);
console.log('resolve run middleware');
console.log(request.body);
});
console.log('resolve all runMiddlewares');
resolve();
} catch (e) {
reject(e);
}
});
}
I have written this function runMiddlewares which should ideally resolve() when all the middleware.process() have resolved. I am using typescript await functionality but it doesn't seem to be working.
I expect something like this to happen inside route.middlewares.forEach(
'begin run middleware'
then awaits resolves
'resolve run middleware'
This continues for all of the middlewares in forEach loop and when the list is all done then and only then 'resolve all runMiddlewares' should be printed and finally, private runMiddlewares( ... ) should resolve.
But instead, forEach is now getting immediately resolved thus preventing all of the middlewares to even complete.
How should this be handled? I thought that await will take care of it inside the forEach loop and only then resolve of runMiddlewares will be called in the end.
What am I missing here?
You can use map to create an array of promises from your middlewares. This array of promises can ben handed to Promise.all which resolves when every single promise of the array has been resolved.
await Promise.all(route.middlewares.map((middleware: IMiddleware) => {
console.log('begin run middleware');
const promise = middleware.process(request, response);
console.log('resolve run middleware');
console.log(request.body);
return promise
});
Or even more compact:
runMiddlewares(...) {
return Promise.all(
route.middlewares.map((middleware: IMiddleware) => {
return middleware.process(request, response))
})
)
}
So, following the recommendation ( https://cn.eslint.org/docs/3.0.0/rules/no-await-in-loop ), I wrote it as follows
private runMiddlewares(route: Route | ExceptionRoute, request: HttpRequest, response: HttpResponse): Promise<any> {
return new Promise(async (resolve, reject) => {
try {
const middlewarePromiseArry: any[] = [];
route.middlewares.forEach((middleware: IMiddleware) => {
console.log('begin run middleware');
middlewarePromiseArry.push(middleware.process(request, response));
// removed the use of await inside of forEach loop following this link: https://cn.eslint.org/docs/3.0.0/rules/no-await-in-loop
// await middleware.process(request, response);
// console.log('resolve run middleware');
// console.log(request.body);
});
await Promise.all(middlewarePromiseArry);
console.log('resolve all runMiddlewares');
resolve();
} catch (e) {
reject(e);
}
});
}
I am happy to accept further answers and recommendations/improvements :)
I've got multiple promise' that I want to run one after the other, and I'm not sure I want to be returning the promises as it gets pretty messy!
So I decided to use the async library and implement the parallel method. Now I noticed that all my promises weren't running one, after the other, instead they were doing what promises are suppose todo (run + finish whenever).
I noticed that all the console.logs were running before all the promises were finished.
async.parallel([
(cb) => {
console.log ("hi")
grabMeData (Args)
.then ( (data) => {
// The promise is done and now I want to goto the next functio
cb();
}).catch(()=>console.log('err'));
},
(callback) => {
// The above promise is done, and I'm the callback
Query.checkUserExists()
.then ( () => {
if (Query.error) {
console.log (Query.error); // Determine error here
return; // Return to client if needed
}
callback();
});
},
() => {
// The above promise is done and I'm the callback!
// Originally wanted to be async
if (Query.accAlreadyCreated) {
this.NewUserModel.user_id = Query.user_id;
this.generateToken();
} else {
console.log ("account not created");
}
console.log ('xx')
}
], () =>{
console.log ("finished async parallel")
});
Any reason why my callbacks are being run before the promises are resolved (.then).
like Bergi said, async.js is redundant when you use promise, your code can be simplified as:
console.log('hi')
grabMeData(Args)
.catch(e => console.error(e))
.then(() => Query.checkUserExists())
.then(() => {
if (Query.accAlreadyCreated) {
this.NewUserModel.user_id = Query.user_id
return this.generateToken()
}
console.log ("account not created")
})
.then(() => console.log ('xx') )