How can I resume a function after a promise fullfilment? - javascript

I’m trying to set up a js messenger chatbot linked to a database.
Of course, sometimes, user requests data from the database to the chatbot and I do use promise to gather it. I recently learned about them, so I do only know the basics.
My issue is that when the promise is fulfilled, I not only need to trigger some code (which is done through the then() function) but also to return data to the main module in order to create an answer.
How can I resume my function (in order to reach the return) after the promise is fulfilled ?
I'm probably having a difficult time being understood so this is a sequence diagramm of my chatbot and some bits of my code.
Main process of the database request
app.js
const Database = require("./services/database");
const Message = require("./services/message");
app.get("/endpoint", (req,res)=>{
let message
//Check if the user message request data
if (req == datarequest)
{
message = Database.request();
}
else
{
message = "You did not require any data";
}
Message.sendToUser(message);
});
DBInteractionModule.js
async request()
{
//Creating the promise
let promise = new Promise(
function(){
let connection = db.connect();
return connection.query("SELECT * FROM user;");
}
);
// Only(?) way to use data when it returns
promise.then(function(data)
{
console.log("data requested : "+ data);
});
//I need to use this return in order to give data back to the main app
return promise;
}
Thanks :)

Promises are tricky the first time you use them, but once you get use to them, it's all about waiting then resolving or rejecting the promise.
I'm assuming, db.connect and connection.query both return a promise, so you have to wait for them to fulfill there promises.
// the `async` word before the functions here is important
const request = new Promise(async (resolve, reject) => {
// wait for the db to connect
let connection = await db.connect();
// wait for the db to return it's data
const data = await connection.query("SELECT * FROM user;");
// after waiting, if no error, the data should be available now
console.log('Requested data (WITHIN the promise) : ', data);
//return the data by resolving the main promise
resolve(data)
});
Usage :
request.then(data => {
console.log('Requested data (OUTSIDE the promise) : ', data);
//Process data
});
Or inside another async function :
async function main() {
const data = await request;
console.log('Requested data (OUTSIDE the promise) : ', data);
}

Related

How to wait for function to execute and get proper promise response?

I'm creating website using Johnny-five,React and node.js to control my Arduino board but I got stuck on handling async/await function. So, user is sending chosen port (COM1) for example to server, server then creates new instance of board
async function checkPortConnection(port) {
let board = new five.Board({port: port});
let success;
await board.on('error', () => {
success = false;
});
await board.on('ready', () => {
success = true;
});
return success;
}
I've thought that keyword await will stop function execution and wait for board response which takes about 7 seconds, but when I do this:
checkPortConnection(port).then((data)=>{
console.log(data);
});
I'm getting 'undefined', (because I'm getting success which is undefined?)
And after that server will send response if chosen port is correct or not.
But my question is, how to get proper response from checkPortConnection() function?
I think the issue is that you are listening for events, but this in and of itself isn't a Promise. Also, if they would be and you would use await you would never reach the code to register the ready event. The following should fix that issue:
async function checkPortConnection(port) {
return new Promise((resolve, reject) => {
let board = new five.Board({port: port});
board.on('error', error => resolve( false ));
board.on('ready', event => resolve( true ));
});
}
Personally I would also do the following, as the Promise will either use then or catch later, so you might want to ignore the boolean bit altogether:
board.on('error', reject);
board.on('ready', resolve);

Trying to wrap zlib deflate into async await

I'm trying to refactor some code to remove .then pyramid and I need some help.
I'm trying to zip a JSON body to pass into a fetch call. I've actually managed this already, and called the fetch withing the zlib callback, but the code is becoming messy.
So, I've been looking at wrapping the zlib call in an async await wrapper. E.g.
async function syncCompressBody(body) {
//return await compressBody(body);
const compressedData = await compressBody(body);
console.log("compressedData");
console.log(compressedData);
return compressedData;
}
function compressBody(body) {
return new Promise( function( resolve, reject ) {
zlib.deflate(body, (err, buffer) => {
if(err){
console.log("Error Zipping");
reject(err);
}
console.log("Zipped");
resolve(buffer);
});
});
}
The compressBody function returns a promise. The reject and resolve calls are in the callback of zlib.deflate.
I actually call syncCompressBody with the JSON to compress. The returned value is the result of the resolve call from compressBody.
The two functions are in a helper library. In my webpage, as part of the submit action, I have...
console.log(jsonContent);
const compressedBody = syncCompressBody(jsonContent);
console.log(compressedBody);
console.log("should be zipped by now..." + compressedBody);
However, the 'should be zipped' message is displayed before the 'zipped' message you can see in the compressBody function. What I actually want is for the code wait in syncCompressBody before returning to and resuming the JS in the submit action.
Edits following feedback....
Based on Bergi's and Liam's comments, I came up with this example of several awaits with each function relying on the previous function....
function awaitStyle2x(){
console.log("pre-awaitStyle2 start");
(async () => {
console.log("awaitStyle2 start")
const t = await theFirstAsyncFunctionX("pass it on");
const u = await theNextAsyncFunctionX(t);
const v = await aThirdAsyncFunctionX(u);
console.log("awaitStyle2 finshed - " + v)
})().catch(e => { /* handle the error */});
console.log("post-awaitStyle2 finished") ;
}
The pre- comment is displayed first, then awaitStyle2 start, then the console log message in function theFirstAsyncFunctionX, then the post- message.
I can see now why my code has the race condition, am I on the right track?
I had a similar use case with gzip, let me know if this helps:
const util = require('util');
const zlib = require('zlib');
const deflate = util.promisify(zlib.deflate);
console.log(jsonContent);
const compressedBody = await deflate(jsonContent);
console.log(compressedBody);
console.log("should be zipped by now..." + compressedBody);

How to use response from three different API calls in node js at one time

I'm trying to create a web application using nodeJS and I'm stuck because of the asynchronous nature of nodeJS.
I have three different environments and based on user input from html form I should check if a user exists in the selected environment.
The html will have 3 check boxes and user can select any number of environments.
if(Dev_Environmnet){
getUserDatafromEnvironment(user,environment, function(callback1)){
if(callback1.error){
// User Does Not Exist Or credentials are wrong
}
else{
//get User API
}
});
}
if(PVS_Environmnet){
getUserDatafromEnvironment(user,environment, function(callback1)){
if(callback1.error){
// User Does Not Exist Or credentials are wrong
}
else{
//get User API
}
});
}
if(Prod_Environmnet){
getUserDatafromEnvironment(user,environment, function(callback1)){
if(callback1.error){
// User Does Not Exist Or credentials are wrong
}
else{
//get User API
}
});
}
once this is done I need those results from the callbacks to print it to another HTML page. this is what I need to show on the next page
DEV - API key gdeuysgdsgddiudgqwywdgAguiegdhsiw
pVS - user does not exist or credentials are wrong
Prod - APYI Key ugehdfiyugfugew%$%$udisfiuhygyig
I'm not able to get all these values at once. Can someone help me?
First, you can wrap your getUserDatafromEnvironment so that it returns a Promise:
function getUserDatafromEnvironmentP(flag, user, environment) {
if (!flag) return Promise.resolve(undefined); // method not selected
return new Promise(function(resolve) {
getUserDatafromEnvironment(user, environment, function(callback) {
resolve(!callback.error);
})
});
}
You can then call your three methods and pass them to Promise.all() which will wait until all three have completed and tell you the results:
let p1 = getUserDatafromEnvironmentP(Dev_Environmnet, user, environmnet);
let p2 = getUserDatafromEnvironmentP(PVS_Environmnet, user, environmnet);
let p3 = getUserDatafromEnvironmentP(Prod_Environmnet, user, environmnet);
let result = Promise.all([p1, p2, p3]).then(
function(results) {
// results[0 .. 2] will contain the three results
// undefined for "not used", false for "failed", true for "ok"
.. continue with processing here
})
);
// you can't do anything useful here because the above code is async
NB: generally I'd frown up using a resolve to indicate failure, but in this case it seems appropriate because of subsequent use of Promise.all().
Not sure what you mean by "get them at once" if you mean "know when all 3 async operations are completed", then there are a few ways you could do this, suggest you look into turning your async methods into promises (How do I convert an existing callback API to promises?) and use Promise.all or look at using async and await.
Based on your pseudo code, something like this.
// I assume that you need special functions to get the information
// from different providers.
function getUserFromPVS(user) {
return new Promise( (resolve, reject) => {
// special code to get user from PVS
return resolve(userdata);
});
}
function getUserFromDev(user) {
return new Promise( (resolve, reject) => {
// special code to get user from Dev
return resolve(userdata);
});
}
function getUserFromProd(user) {
return new Promise( (resolve, reject) => {
// special code to get user from Prod
return resolve(userdata);
});
}
// Converted the call to getUserDatafromEnvironment so it is a promise.
function getUserData(flagSelected, user, environment, req) {
return new Promise( (resolve, reject) => {
if (!flagSelected) return resolve(null); // Returns null if not selected
getUserDatafromEnvironment(user,environment, (cb) => {
if (cb.error) {
return resolve(null); // Return NULL instead of rejecting.
} else {
// Call to promise that return user information
// I assume you need different functions for
// each provider
return resolve( req(user) )
}
})
});
}
Promise.all([
getUserData( PVS_Environmnet, user, environment, getUserFromPVS ),
getUserData( Dev_Environmnet, user, environment, getUserFromDev ),
getUserData( Prod_Environmnet, user, environment, getUserFromProd ),
]).then( (results) => {
// results[0] has result from Dev or is null
// results[1] has result from PVS or is null
// results[2] has result from Prod or is null
}).catch(
(error) => console.error(error)
)
You should keep calls to the other checks inside callbacks from previous ones:
checkOne(someArgs, (callback) => {
let checkOneResult = true;
if (callback.error)
checkOneResult = false;
checkTwo(someArgs, (callback) => {
let checkTwoResult = true;
if (callback.error)
checkTwoResult = false;
//here I got result from both checks:
console.log(checkOneResult);
console.log(checkTwoResult);
});
});

Chaining Asynchronous Functions Node.js bluebird mongoskin

I have been reading many posts on how to chain asynchronous functions but I just can't seem to get it right!
As the title indicates. I am trying to chain mongoskin database calls together, so that i can gather all the information in chunks and then finally send the accumulated result in the response.
I have the object user as :
var User = {
username: 'someusername',
accounts: [{name: 'account_1'}, {name: 'account_2'}]
}
For each of the accounts I need to gather data and then send the accumulated data in the response. So i am using the following for loop to iterate over the accounts:
var promise = require('bluebird');
var db = require('mongoskin').db('mongodb://localhost/someDB');
for(var x in user.accounts){
//Fetch account data
user.accounts[x].accountData = fetchAccountData(user.accounts[x].name);
}
//Finally send the collected response
response.send(user);
And the function fetchAccountData looks like the following:
function fetchAccountData(screen_id){
db.collection('master')
.aggregate([
{$match: {screen_id: screen_id}}
], function(err, res){
if(err)
return null;
else{
console.log('Done', screen_id);
return res;
}
});
}
How can i chain this to have the following algorithm:
start:
for each account:
fetchDataForAccount
Finally:
Send Response
Your algorithm can be achieved using the following code:
var Promise = require('bluebird');
var mongo = require('mongoskin'), db;
Promise.promisifyAll(mongo.Collection.prototype);
db = mongo.db('mongodb://localhost/someDB');
Promise.all(user.accounts.map(function(acct) {
return fetchAccountData(acct.name).then(function(data) {
acct.accountData = data;
});
}))
.then(function() {
response.send(user);
})
.catch(function(err) {
// handle error
});
function fetchAccountData(screen_id){
return db
.collection('master')
.aggregateAsync([
{$match: {screen_id: screen_id}}
]);
}
EDIT: Here's a breakdown of the code
The first thing you need to do is ensure that aggregate returns a Promise instead of using a continuation (e.g. callback). You can do this by using bluebird's amazing promisification abilities :) Here we use it on mongo.Collection.prototype so that when collection() is called it will return a promise-capable collection instance. Then we have fetchAccountData return the promise returned by aggregateAsync so the client has a way of knowing when that promise is resolved.
Next, we map over each account in accounts and return a promise which will be fulfilled once the account data is fetched and it has been assigned to the account object. We then use Promise.all which will return a promise that is fulfilled "when all the items in the array are fulfilled" (from the docs).
Finally, we have to use then() to "wait" until the promise returned from all has resolved, and the finally send back the response with the complete user object.

Chek the return from a promise function before proceeding. Wrong approach?

Background: I have a PHP background and this is my first application using MEAN stack.
I need to save a record but before I must to check if there is any record under the same id already saved in the DB.
In PHP I would do something like this:
Once the user clicks "Save":
1) Call the function to check if an entry with that id already exists
2) If it doesnt, call the save function.
In Javascript, I'm getting a little confused with Promises and so on.
Can somebody give me some light here?
Right now, I'm doing the following:
In the save api, I call this function to check if the record already exists in the DB:
recordExists = findTranscationByBill(billId);
function findTransactionByBill(billId){
results = new promise(function(resolve, reject){
Transactions.find({billId : billId},function(err, transactions){
if(err)
reject("Error: "+err);
//console.log(transactions);
resolve(transactions);
});
});
results.then(function(data){
console.log('Promise fullfilled: '+ data);
}, function(error){
console.log('Promise rejected: ' + error);
});
return $results;
}
The problem is that I think I'm not using promise properly, as my variable doesn't get populated (because its Async).
In the console.log I see that the promise is being fulfilled however, the variable returns as [object Object]
I'm stucked with this problem because I don't know if I should carry on thinking as PHP mindset or if there is a different approach used in Javascript.
Thanks in advance!
In my opinion you could just as well use a callback for this, and since MongoDB has a count method, why not use it
function findTransactionByBill(billId, callback){
Transactions.count({billId : billId}, function(err, count){
if (err) {
callback(err, false);
} else {
callback(null, count !== 0);
}
});
}
and to use it
findTransactionByBill(billId, function(err, exists) {
if (err) {
// handle errors
} else if ( ! exists ) {
// insert into DB
}
}
I think the right function is:
function findTransactionByBill(billId){
var results = new promise(function(resolve, reject){
Transactions.find({billId : billId},function(err, transactions){
if(err) {
reject(err);
} else {
if (transactions.length === 0) {
reject('No any transaction');
} else {
//console.log(transactions);
resolve(transactions);
}
});
});
results.then(function(data){
console.log('Promise fullfilled: '+ data);
}, function(error){
console.log('Promise rejected: ' + error);
});
return results;
}
And then use it like this:
recordExists = findTranscationByBill(billId);
recordExists.then(function() {
// resolved, there are some transactions
}, function() {
// rejected. Error or no any transactions found
// may be you need to check reject result to act differently then no transactions and then error
});
I assume you are using mongodb native drive.
I think mongodb doesn't have promise built-in supported. So you have to promisify it by a little help from promise library. Please refer this if you want to use bluebird.
After promisifying, the code should looks like that (using bluebird):
Promise = require('bluebird');
// Promisify...
var _db = null;
var client = MongoClient.connectAsync('mongodb://localhost:27017/test')
.then(function(db) {
_db = db
return db.collection("myCollection").findOneAsync({ id: 'billId' })
})
.then(function(item) {
if (item)
_db.save(item);
})
.catch (err) {
// error handling
}
The above code is not perfect, because it introduced a global var, so the better version may be
Promise = require('bluebird');
// Promisify...
var client = MongoClient.connectAsync('mongodb://localhost:27017/test')
.then(function(db) {
return Promise.prop({
item: db.collection("myCollection").findOneAsync({ id: 'billId' },
db: db
})
})
.then(function(result) {
var item = result.item;
var db = result.db
if (item)
db.save(item);
})
.catch (err) {
// error handling
}
You need to check bluebird to know how to use it. Also they are many other promise libraries like q, when, but all are similar stuff.

Categories

Resources