Combine two callbacks into one return - javascript

So I have this code:
module.exports.getEstimate = (event, context, callback) => {
var data = JSON.parse(event.body);
lalamove.getQuotation(data ,context, function(err, llm_data){
callback(null,llm_data)
});
};
So it calls lalamove.getQuotation function and returns an object:
{ "totalFee": "108", "totalFeeCurrency": "PHP" }
Now, I have added a new function, that returns this object:
{ "totalFee": "10", "totalFeeCurrency": "PHP" }
from a different function so I thought I should push them in one array and then that is when I would call the callback but it does not work, this is what I have tried
module.exports.getEstimate = (event, context, callback) => {
var data = JSON.parse(event.body);
var response = []
lalamove.getQuotation(data ,context, function(err, llm_data){
const llm_obj = { "lalamove": llm_data }
response.push(llm_obj);
});
inhouse.getQuotation(data ,context, function(err, ih_data){
const ih_obj = {"inhouse": ih_data }
response.push(ih_obj);
});
callback(null,response);
};
and what I want to be the response is like this:
["lalamove": { "totalFee": "108", "totalFeeCurrency": "PHP" },
"inhouse": { "totalFee": "10", "totalFeeCurrency": "PHP" }]
what am I doing wrong?

Your callback(null,response) will not wait for those two callback functions to finish. You can use Promise and use Promise.all(objs).then(function) to wait for all promises finish and run.

Welcome to World's Javascript world - Callback hell.
We have some options for your case: Callback hell, async lib, Promise, async/await...
Callback hell: Call a async function in a callback
module.exports.getEstimate = (event, context, callback) => {
var data = JSON.parse(event.body);
var response = []
lalamove.getQuotation(data, context, function (err, llm_data) {
const llm_obj = { "lalamove": llm_data }
response.push(llm_obj);
// lalamove.getQuotation done!
// call next action
inhouse.getQuotation(data, context, function (err, ih_data) {
const ih_obj = { "inhouse": ih_data }
response.push(ih_obj);
// inhouse.getQuotation done!
// call the last action
callback(null, response);
});
});
};
Async lib: async
You can use waterfall function to do actions in order, and parallel if order is not matter.
module.exports.getEstimate = (event, context, callback) => {
var data = JSON.parse(event.body);
var response = []
async.parallel([
function (next) {
lalamove.getQuotation(data, context, function (err, llm_data) {
// TODO: check err object
const llm_obj = { "lalamove": llm_data }
response.push(llm_obj);
// lalamove.getQuotation done!
// do next action
next();
});
},
function (next) {
inhouse.getQuotation(data, context, function (err, ih_data) {
const ih_obj = { "inhouse": ih_data }
response.push(ih_obj);
// inhouse.getQuotation done!
// do next action
next()
});
}
], function (err) {
// TODO: check err object
// call the last action
callback(null, response);
});
};

Try wrapping two quotation calls in Promise, then utilise Promise.all to wait for both of them to be completed, then return the result to the callback
module.exports.getEstimate = (event, context, callback) => {
let data = JSON.parse(event.body);
// wrap quotation calls in `Promise`
Promise.all([
new Promise(resolve => lalamove.getQuotation(data, context, (err, lalamove) => resolve({ lalamove }))),
new Promise(resolve => inhouse.getQuotation (data, context, (err, inhouse ) => resolve({ inhouse }))),
]).then(response => {
// return the result back to `callback`
callback(null, response);
})
};

You could also try using util.promisify and the async / await syntax.
For example:
const util = require("util");
module.exports.getEstimate = async (event, context, callback) => {
let data = JSON.parse(event.body);
try {
let response = await Promise.all([ util.promisify(lalamove.getQuotation)(data, context),
util.promisify(inhouse.getQuotation)(data, context) ]);
callback(null, response);
} catch (err) {
callback(err);
}
};
We can also do something similar, but without async / await:
const util = require("util");
const getEstimate = (event, context, callback) => {
let data = JSON.parse(event.body);
Promise.all([util.promisify(lalamove.getQuotation)(data, context),
util.promisify(inhouse.getQuotation)(data, context)])
.then(response => callback(null, response))
.catch(err => callback(err));
};

Related

Second function not called asynchronously in nodejs

I am trying to call some function using a single express router , I want to call them in order, meaning that I don't want getLaps() function to execute before get streams function has done all the work , so I tried to use some solutions I found on the internet but it didn't work, the second function doesn't execute. Please help.
Here is my code :
router.get("/", async (req, res,done) => {
res.status(201).send('created user')
return getLaps(function () {
getStreams(function () {
});
});
// await getStreams();
// await getLaps();
// console.log("hey")
});
Here is the get laps function :
function getLaps(req) {
const access_token = '75f2d92fdc445033312854d775e039b6c5bf04e7';
//for test 3756582581,
const idL = [5567017025, 5566531480];
const stravaClient = StravaClientService.getClient(access_token);
const activityService = StravaActivityService(stravaClient);
var params = {
TableName: "run-id",
Key: {
"id": "15428785",
}
};
console.log("cool laps")
docClient.get(params, async function (err, data) {
if (err) {
console.log("Error", err);
} else {
}
idL.map((id, index) => setTimeout(() => activityService.listLaps(id), (5 + index) * 60)
)
//data.Item.json
});
}
and the streams function :
function getStreams(req) {
const idS = [
5567017025, 5566531480
];
const stravaClient = StravaClientService.getClient(access_token);
const activityService = StravaActivityService(stravaClient);
var params = {
TableName: "run-id",
Key: {
"id": "15428785",
}
};
console.log("cool streams")
docClient.get(params, async function (err, data) {
if (err) {
console.log("Error", err);
} else {
idS.map((id, index) => setTimeout(() => activityService.streamActivity(id), (5 + index) * 60))
console.log("got the streams")
}
});
}
in your getStream and getLaps function return promises instead of other object/Stuff like
async function getStream(){
return new Promise(async (resolve, reject){
//Do something
//where you want to return something just call resolve function like
resolve()
//if you want some output of getStream() just pass it to resolve function
//const result = 'I'm result'
resolve(result)
})
}
do same thing with the laps function and in your router call them with await keyword

How to use callback in .then() function in Nodejs?

I have nodejs module to fetch data from mongodb database using mongodb driver. Callback is passed to given function which return a promise, but instead of returning result in .then() function, it is passing value to callback function. How can I call this function from other module or function since it is not returning it in .then()? I tried to console the result of .then(), but it is showing undefined.
const MongoClient = require('mongodb').MongoClient;
const Db = require('../model/db');
Db.findUser = (details, callback) => {
return dbconnection().then(db => {
if (db) {
return db.collection('users').findOne({
email: details.email,
pass: details.password
}).then(data => {
if (data) {
console.log('Found one');
callback(true);
} else {
let err = new Error();
callback(err);
}
})
}
I have used following function to call the promise. I am new to promises.
var getUser = function(callback) {
db.findUser().then(result => {
console.log(result) // undefined
})
}
You can easily do it using async/await. Something like this:
Db.findUser = async (details, callback) => {
const db = await dbconnection();
const data = await db.collection('users').findOne({
email: details.email,
pass: details.password
});
if (data) {
console.log('Found one');
callback(true);
} else {
let err = new Error();
callback(err);
}
return data;
}
and consume it like:
const getUser = async (details, callback) => {
const data = await Db.findUser();
// do whatever you need with data
return data;
}

The value from the function is returned after the variable is output to the console

In the loop, I call the function and pass it the identifier, after which the function should return the vacancy to me.
const Profile = require('upwork-api/lib/routers/jobs/profile.js').Profile;
...
let jobsDetails = [];
for (let valueKeys of Object.values(arrayOfJobs)) {
getJobDitalis(api, valueKeys, (error, data) => {
console.log(data.profile)
jobsDetails.push(`${data.profile}`);
});
console.log(jobsDetails)
}
...
function getJobDitalis(api, key, callback) {
const profile = new Profile(api);
profile.getSpecific(key, (error, data) => {
callback(error, data);
});
}
But for some reason, first an empty object is displayed in the console, and then only information about the vacancy. That is, console.log (jobsDetails) is fired first, and then only console.log (data.profile)
[]
[]
[]
{job}
{job}
{job}
Why it happens? What is my mistake?
Using async await you can make your async task synchronous.
I don't know about your code structure, but I tried to make a solution from the given data.
function getJobDitalis(api, key, callback) {
const profile = new Profile(api);
profile.getSpecific(key, (error, data) => {
callback(error, data);
});
}
...
(async ()=> {
let jobDetails = [];
for (let valueKeys of Object.values(arrayOfJobs)) {
const profile = await new Promise(resolve=>{
getJobDitalis(api, valueKeys, (error, data) => {
console.log(data.profile)
resolve(data.profile);
});
})
jobDetails.push(profile)
}
console.log(jobDetails);
//do your task with jobDetails
})()
I made IIFE async function.
Flow (let's assume a single iteration of the for loop):
getJobDetalis(api, key, callback)
profile.getSpecific(key, func)
func(error, data)
callback(error, data)
console.log(data.profile)
console.log(jobsDetails)
However, profile.getSpecific does not necessarily wait for func to complete.
And since the 1st print is executed from the call-stack of func, it will not necessarily occur before the 2nd print.

Testing async SQS sendMessage

I have an async lambda, which performs an async SQS sendMessage request. The SQS queue is a standard queue, not FIFO, just to clarify.
Here's an example of code (without irrelevant part of the logic):
exports.functionHandler = async (event, context, callback) => {
try {
let parsedBody = JSON.parse(event.Records[0].body);
let modifiedBody = await doStuff(parsedBody);
let sqsPayload = {
MessageBody: JSON.stringify(modifiedBody),
QueueUrl: my-queue-url
};
await sqs.sendMessage(sqsPayload).promise();
callback(null, utils.respondSuccess("Done"));
} catch (err) {
// Handle error
callback(null, utils.respondError(err));
}
};
const doStuff = async payload => {
// Do stuff
}
Pretty simple.
Now the problem: I'm trying to test this function using the package aws-sdk-mock. This is how I was stubbing the sendMessage function when the lambda wasn't async and the sendMessage function was using the callback:
it("an awesome title for my test", async () => {
let payload = {
Records: [
// Data here
]
};
AWS.mock("SQS", "sendMessage", (param, callback) => {
let response = {
ResponseMetadata: {
RequestId: "test-request-id"
},
MD5OfMessageBody: "a892e8d8589e97ca92fb70020f01c16c",
MessageId: "test-message-id"
};
callback(null, response);
});
await app.functionHandler(payload, {}, (err, result) => {
let parsedBody = JSON.parse(result.body);
expect(parsedBody.message).to.be.equal("Done");
// More stuff
});
AWS.restore();
});
If I use this test, the sendMessage function throws the following error:
sendMessage returned an invalid MD5 response. Got "undefined", expecting "a892e8d8589e97ca92fb70020f01c16c".
I'm not sure how to test sendMessage asynchronously. I don't mind adopting a different package if it helps me to get the job done.
Can anyone help?
Thanks a lot
I've not used aws-sdk-mock but apparently in your mock you are using callback and in the lambda handler it is an async call. I use proxyquire for mocking dependencies. Here is an example:
functionHandler.js
Don't need to use callback and context in Lambda runtime Node8.10.
let AWSSQS = require('aws-sdk/clients/sqs');
let sqs = new AWSSQS();
exports.functionHandler = async (event) => {
// No need to use callback when Lambda runtime is 8.10.
try {
let parsedBody = JSON.parse(event.Records[0].body);
let modifiedBody = await doStuff(parsedBody);
let sqsPayload = {
MessageBody: JSON.stringify(modifiedBody),
QueueUrl: my-queue-url
};
await sqs.sendMessage(sqsPayload).promise();
return utils.respondSuccess('Done');
} catch (err) {
throw utils.respondError(err);
}
};
test.spec.js
Pretty much self explanatory. Your define an object with name of dependency as property.
const proxyquire = require('proxyquire');
let app = require('path/to/function');
describe('SQS', () => {
it("an awesome title for my test", async (done) => {
const app = proxyquire(app, {
'aws-sdk/clients/sqs': function() {
this.sendMessage = (params) => {
return {
promise: () => {
return Promise.resolve({
ResponseMetadata: {
RequestId: 'test-request-id'
},
MD5OfMessageBody: 'a892e8d8589e97ca92fb70020f01c16c',
MessageId: 'test-message-id'
});
}
}
}
}
});
let payload = {
Records: [
// Data here
]
};
const data = await app.functionHandler(payload);
let parsedBody = JSON.parse(data.body);
expect(parsedBody.message).to.be.equal("Done");
done();
});
});

how to trigger a async process one after another

How I should modify the following code, so I can make sure Process3 is triggered after Process2.update or Process2.create completed?
The main purpose for following code is I want to makeProcess1 finished. Then check if id exist, if yes, Process2.update is triggered. if not, Process2.create is triggered.Once Process2 finished, check if cmd existed. if yes,triggered Process3.
run: function (req, res) {
if (req.session) {
const values = req.params.all();
const id = values.id;
const cmd = values.cmd;
const param = _.omit(values, ['cmd', 'id']);
const cb1 = (e, d) => {
if (e) {
console.log(e);
res.status(400).send({ e });
} else {
Process1(values);
res.status(200).send({ d });
}
};
const cd2 = (id, param, cb1) => {
if (id) {
Process2.update({ id }, param, cb1);
} else {
Process2.create(param, cb1);
}
};
if (cmd) {
cd2(id, param, cb1, Process3(values, cmd));
}
else {
cd2(id, param, cb1);
}
} else {
res.status(403).send({ e: 'Forbidden access.' });
}
}
try approach by following, but not sure how I can pass argument id, params to Process2 and process3
let async = require('async');
const Process1 = (value, cb) => {
console.log("Process1()");
console.log(value);
cb(null, value + 1);
};
const Process2 = (value, cb) => {
console.log("value(): wait 5 sec");
console.log(value);
cb(null, value+10);
};
const Process3 = (value, cb) => {
console.log(value);
console.log("Process3(): wait 5 sec");
cb(null, value+100);
};
let Pro_1_2 = async.compose(Process2, Process1);
let Pro_2_3 = async.compose(Process3, Process2);
Pro_1_2(1, (e, r) => {
Pro_2_3(r, (error, result) => {
console.log(result);
});
});
The code you posted in your original question seems pretty twisted up, so I'm not going to attempt to rewrite it, but in general if you want to perform asynchronous calls which depend on each other, async.auto is a good way to go. Rather than declaring variables at the top that you attempt to mutate via some function calls, it's better to make Process1, Process2 and Process3 asynchronous functions that call their callbacks with a new values object. Something like:
async.auto({
doProcess1: function(cb) {
// Assuming Process1 calls `cb(undefined, newValues)` when done.
Process1(values, cb);
return;
},
doProcess2: ['doProcess1', function(results, cb) {
if (results.doProcess1.id) {
Process2.update({id: results.doProcess1.id}, cb);
return;
} else {
Process2.create(_.omit(results.doProcess1, ['cmd', 'id']), cb);
return;
}
}],
doProcess3: ['doProcess2', function(results, cb) {
if (results.doProcess2.cmd) {
Process3(results.doProcess2, cb);
return;
}
else {
cb(undefined, results.process2);
return;
}
}]
}, function afterProcess3(err, results) {
// Handler err or process final results.
});
Note all the return calls. They're not strictly necessary, but good practice to avoid accidentally running more code after calling your asynchronous functions.
Have you considered using "compose", from async.js?
const a = (data, cb) => {
var result = 'a';
cb(null, result);
};
const b = (data, id, cb) => {
var result = 'b';
cb(null, result);
};
const c = (data, cb) => {
// stuff to do with result
};
var aThenC = async.compose(c, a);
var bThenC = async.compose(c, b);
if (useA) {
aThenC(data, (result) => {
// result from c
res.status(200).send(result);
});
} else {
bThenC(data, id, (result) => {
// result from c
res.status(200).send(result);
});
}
In this scenario, a and b are your Process2 create and update, respectively, and c is the callback to Process3, if I understood correctly.
EDIT: You'll only have to enter the initial parameters (e.g. register ID) on the composed function. What composes really do is this: a(b(c(param))). That param is basically everything you need to start the process. The parameters for the following functions will be set inside the function before that.
I'll add code to support it as soon as I'm on a keyboard.

Categories