Firebase onCall method finishes before code execution completes - node.js

I have a rather intensive Node function that includes multiple aync tasks that waits for the previous action to complete prior to going to the next step. It's crucial that the entire task finishes when it is called. I'm seeing issues where the onCall function will end before the code execution finishes. I can see this in the logs, as I'm console logging the various methods called from top to bottom until the function is "complete". The parent function is here:
exports.myHandyFunction = functions.https.onCall((data, context) => {
return new Promise((resolve, reject) => {
const response = async () => {
try {
let result = await some_main_function_elsewhere_in_code();
resolve(result)
} catch (err) {
console.log('parent promise caught error:')
console.log(err)
reject(err)
}
}
response()
})
})
I've increased the function's timeout from 60s to 240s hoping that this is purely a function timeout issue? This is pretty troubling, as I have some complex logic being called that ends up not fully completing. Any help would be greatly appreciated.

Your response function is async. When you call it, you're not awaiting for it's execution. This is leading on the premature end of the oncall method execution.
Instead of wrapping the business logic inside an async function, wrapped inside a promise, you can change the code like this:
exports.myHandyFunction = functions.https.onCall((data, context) => {
return new Promise(async (resolve, reject) => {
try {
let result = await some_main_function_elsewhere_in_code();
resolve(result)
} catch (err) {
console.log('parent promise caught error:')
console.log(err)
reject(err)
}
})
})
Basically, the Promise is declared as async, so we can use the await constructor inside of it. Then, we just have to call the some_main_function_elsewhere_in_code method, awaiting for it.

Related

for loop does not stop even after condition is met [Node, asyn await]

I am trying to stop for loop on certain condition but loop continuous even if use break on certain condition.
Following is the code,
let statusKey = 0;
const terminateLoop = async function () {
statusKey = 1;
mainFn()
}
const doSomething = async function () {
for (let i of temAnotherArr) {
await new Promise((resolve, reject) => {
execFile(`/temp/abc.sh`, (error, stdout, stderr) => {
console.log(error)
if (error) {
reject();
} else {
resolve()
}
});
});
}
}
const mainFn = async function () {
for (let i of tempArr) {
if (statusKey === 1) {
doSomething();
return;
} else {
await new Promise((resolve, reject) => {
execFile(`/temp/xyz.sh`,(error, stdout, stderr) => {
console.log(error)
if (error) {
reject();
} else {
resolve()
}
});
});
}
}
}
mainFn();
Initially mainFn() gets called and it starts looping tempArr, after certain time from UI user clicks on button which trigger terminateLoop() function and statusKey gets change to 1 then same mainFu() gets called. It enters into if condition and calls function doSomething(). But once doSomething() completes, else condition still gets executes. I am not sure why loop is not breaking. I guess it has something to do with async await and Promise. I am not sure how to break this and not sure what I am making mistake.
But once doSomething() completes, else condition still gets executes.
The else will not execute from scratch, but do realise that there can be a pending callback call from that execFile in the else block that is still to happen after statusKey has been set to 1. When eventually that callback is called, your code will still execute that console.log without having regard of statusKey's value.
Furthermore, your code then resolves the promise that is being awaited in the else block and then the execution context of mainFn is resumed in that else block. So yes, it is possible that some code in the else block still executes after statusKey has been set to 1, but it is not possible that the else block is executed from scratch. You can be sure that this execFile function is not called anymore from that particular spot in the code when statusKey is 1. Only its callback could still be called and any statements that follow the await in the mainFn context (your example code has no such statements).
So the easiest "fix" is to also add an if statement inside that callback, and have it test the value of statusKey.
Unrelated, but I would avoid code repetition and promisify the execFile function.

the piece of code will not executed in async wait

the piace of code with if condition will not executed and the parent function will be executed after promise, but I dont understand why
let sql = `SELECT *
FROM ${table}
WHERE trader = '${trader}'
AND pair = '${data.symbol}'`;
console.log(sql)
let resp = await new Promise((resolve, reject) => {
db.all(sql, function (err, rows) {
console.log("err2")
console.log(err)
console.log("rows2")
console.log(rows)
return resolve(rows)
})
})
if (resp[0]) { // <------- this will be executed after
if (data.amount > resp[0].amount) {
console.log('amount cambiato, comprato')
// BUY Position
await updatePosition(data, trader, 'buy');
}
if (data.amount < resp[0].amount) {
console.log('amount cambiato, sellato')
// BUY Position
await updatePosition(data, trader, 'sell');
}
if (data.amount == resp[0].amount) {
// BUY Position
console.log('amount IDENTICO');
await setCheckedTraderCoin(trader, data.symbol)
}
}
why this?
This has to be in an async function since you're using await. Thus, it returns a promise.
So, the caller must use await or .then() on that returned promise so it will also wait for the work to be done here.
ALL async functions return their promise upon the first await in the function. That's why the caller has to also wait for that promise to resolve - otherwise it runs off and executes before any of the asynchronous work in this function is complete (as you've observed).
So, the solution to your caller running before the asynchronous work in this function has completed is for the caller to wait (using either .then() or await) for the returned promise to resolve before it does anything further.
Also, you need error handling on the promise that wraps db.all() so you reject that promise if there's an error:
let resp = await new Promise((resolve, reject) => {
db.all(sql, function (err, rows) {
if (err) {
reject(err);
} else {
resolve(rows)
}
});
});
FYI, it would be preferable to use a driver for your database that directly supports promises so you don't have to manually wrap each call in a promise yourself. Most database interfaces support promises by now. Sometimes, you need a different version of the driver - sometimes it's already available in the one you're using. It depends upon which database/driver you are currently using.

Is it OK to pass an async function to setImmediate in order to call an arbitrary function asynchronously?

I want to call a given function asynchronously. The wrapper function tryCallAsync is one way of doing this. This approach works. However, it requires that the callback for setImmediate to be an async function. This seems wrong, as the callback is returning a Promise that is not used. Is it wrong to pass an async function to setImmediate for this purpose?
async function tryCallAsync(fn, ...args) {
return new Promise((r, j) => {
setImmediate(async () => {
try {
r(await fn(...args));
}
catch (e) {
j(e);
}
})
})
}
// Using tryCallAsync
let resolveAsync = tryCallAsync(()=>{
return new Promise((r,j)=>{
setImmediate(()=>r('resolveAsync'));
});
})
resolveAsync.then((resolve)=>console.log(resolve));
let resolve = tryCallAsync(()=>{
return 'resolve';
});
resolve.then((resolve)=>console.log(resolve));
NB: https://www.youtube.com/watch?v=e3Nh350b6S4
Yes, it's wrong, for multiple reasons:
setImmediate doesn't handle the returned promise, especially it doesn't deal with errors1
Don't put business logic in asynchronous (non-promise) callbacks when using promises. Settle a promise from there, nothing else.
1: And even while your particular callback never rejects the returned promise due to the try/catch, it still feels wrong
Your function should be written as
async function tryCallAsync(fn, ...args) {
await new Promise(resolve => {
setImmediate(resolve);
});
return fn(...args);
}
This approach doesn't waste a Promise, however, still, it's not as performant as the conventional way of doing this.
function tryCallAsync(fn, ...args) {
return new Promise((r, j) => {
setImmediate(() => {
(async function () {
return await fn(...args);
})().then(r).catch(j);
});
});
}

AWS Lambda for db trigger not getting completed

I have created a lambda for dynamodb trigger and it is working ok if I use lambda function with callback but as soon as I change it to async it stops working. It is getting called but call returns before the end of the internal function. I am using await for any async call.
const handler: Handler = async (event: any, context: Context, callback : Callback) => { console.log(event);
try{
// construct request handler
console.log(event.Records);
const createHandler = dummyHandler;
await createHandler.handleRequest(event.Records);
return callback(null, 'Successfully processed ${event.Records.length} records.');
}
catch(err){
console.log(err);
throw err;
}
finally{
console.log('Finally I am here');
}
}
So this code works. Though strangely finally is executed before the handleRequest completes. But still handleRequest does complete as expected.
But since callback is kinda older and unwanted version I am trying to remove it and add promise in my async handler and then it stops working as expected.
const handler: Handler = async (event: any, context: Context): Promise<boolean> =>
{
console.log(event);
try{
// construct request handler
console.log(event.Records);
const createHandler = dummyHandler;
const result = await createHandler.handleRequest(event.Records);
return result;
}
catch(err){
console.log(err);
throw err;
}
finally{
console.log('Finally I am here');
}
}
In my internal function, I am doing few db queries and updates. I am using await in all async calls. But still after my first db query, finally block is being executed. In handler with callback, other db calls still execute but in async version with no callback, after finally is called, nothing else happens and request is ended.
I have checked memory size (256MB) and timeout (300seconds) for lambda function and neither of them are exceeding.
So in case anyone else experiences this issue. I ended up making following changes to my async handler without callback.
const handler: Handler = async (event: any, context: Context):Promise<boolean> => {
console.log(event);
try{
// construct request handler
console.log(event.Records);
const createHandler = new ...();
return await createHandler.handleRequest(event.Records);
}
catch(err){
console.log(err);
throw err;
}
finally{
console.log('Event Record');
}
This seems to be working as expected. All my db queries and updates are completed (basically this call createHandler.handleRequest is ended properly) before the request ends. Though needs extensive testing. Still welcome any feedback.
Basically it was working with promise but jarmod was right, I just needed to return the await call.

Node.js Promise.all when function returns nothing

How to handle multiple calls to the same function when its returning nothing. I need to wait untill all calls are finished so i can call another function.
For now I'm using Promise.all() but it doesn't seem right:
Promise.all(table_statements.map(i => insertValues(i)))
.then(function(result) {
readNodeData(session, nodes);
})
.catch(function() {
console.log(err);
})
function insertValues(statement) {
return new Promise((res, rej) => {
database.query(statement, function (err, result) {
if (err) {
rej(err)
}
else{
console.log("Daten in Tabelle geschrieben")
res(); // basically returning nothing
}
});
});
}
This writes data to a database in multiple statements, i need to wait untill all are finished.
Is this actually the "right" way to do it? I mean... it works, but i have the feeling it's not how you are supposed to do it.
Using Promise.all for your case is a good call, since it returns a Promise, when all the promises passed as an iterable are resolved. See the docs.
However, for brevity and readability, try converting your insertValues into async-await function as follows. This tutorial would be a great place to start learning about async functions in JavaScript.
// async insertValues function - for re-usability (and perhaps easy unit testing),
// I've passed the database as an argument to the function
async function insertValues(database, statement) {
try {
await database.query(statement);
} catch (error) {
console.error(error);
}
}
// using the insertValues() function
async function updateDatabase(database) {
try {
// I am using 'await' here to get the resolved value.
// I'm not sure this is the direction you want to take.
const results = await Promise.all(
tableStatements.map(statement => insertValues(database, statement))
);
// do stuff with 'results'.. I'm just going to log them to the console
console.log(results);
} catch (error) {
console.error(error);
}
}
Here, insertValues() function doesn't return any value. Its operation on the database is entirely dependent on the query statement passed to it. I wrapped it within a try-catch block so as to catch any errors that might arise while performing the operation (s) above. More details on handling errors using try-catch can be found here.
Your promisified write to database looks ok, so we can update code from another part.
Let's rewrite it a little to use async/await and try/catch.
(async() => {
const promisifiedStatements = table_statements.map(i => insertValues(i));
try {
await Promise.all(promisifiedStatements);
readNodeData(session, nodes);
} catch(e){
console.log(e)
}
})();
I use here IIFE to use await behaviour.

Resources