I have the following code:
updateDocument = (data) => {
Cloudant(CONN_STRING, (err, cloudant) => {
if (err) {
throw new Error('Failed to initialize Cloudant: ' + err.message);
}
let db = cloudant.db.use('my-db')
db.insert(data, (err, result) => {
if (err) {
throw new Error('Failed to initialize Cloudant: ' + err.message);
}
else {
console.log(result);
}
})
});
}
I would like for updateDocument() to return the result db.insert provides. However, if I try to return a variable it is undefined (I believe because of the async calls). I've tried async and await, but I may not have set them up properly because they also didn't work.
Thanks for any help!
This is a "JavaScript is asynchronous" problem. You could simplify your updateDocument function like so:
updateDocument = (data) => {
var cloudant = Cloudant({ url: CONN_STRING, plugin: 'promises');
let db = cloudant.db.use('my-db')
return db.insert(data);
}
This is using the Cloudant library with the "promises" plugin. JavaScript Promises help you manage asynchronous calls without passing callback functions around.
You would call this code like so:
updateDocument({a: 1, :b})
.then((data) => {
console.log('success!', data);
}.catch((e) => {
console.log('oops!', e);
});
Success arrives at 'then', failure arrives in the 'catch' clause.
Related
I have written the following code in Nodejs which is saving data in MongoDB:
function insertDoc(db,data){
return new Promise(resolve => {
callback=db.collection('AnalysisCollection').insertOne(data).then(function(response,obj){
console.log("Inserted record");
resolve(obj);
//console.log(obj);
// response.on('end',function(){
// resolve(obj);
// });
//return resolve(obj);
}).then(() => { return obj }
).catch(function(error){
throw new Error(error);
});
})
}
I am calling the above function from the main function like this:
async function cosmosDBConnect(nluResultJSON){
try{
//console.log("Inserting to cosmos DB");
console.log(nluResultJSON);
var url = config.cosmos_endpoint;
var result="";
var data = JSON.parse(JSON.stringify(nluResultJSON));
MongoClient.connect(url, function(err, client) {
assert.equal(null, err);
var db = client.db('NLUAnalysisDB');
// insertDoc(db, data, function() {
result=insertDoc(db, data, function() {
console.log(result);
client.close();
//return data._id;
});
});
}
catch (e) {
console.log(e);
}
}
module.exports = { cosmosDBConnect };
But in cosmosDBConnect, I am getting 'undefined' for the result, though in insertDoc I am getting the output for'obj' with _id for the inserted record.
Please help me to return this _id to cosmosDBConnect.
You are use callbacks inside of async function, which creates internal scopes. So your return aplies to them instead of whole function. You should use Promise-based methods inside of async function using await (without callbacks) or wrap whole function into own Promise otherwise.
Example:
function cosmosDBConnect(nluResultJSON) {
return new Promise((resolve, reject) => {
var url = config.cosmos_endpoint;
var result = '';
var data = JSON.parse(JSON.stringify(nluResultJSON));
MongoClient.connect(url, function(err, client) {
if (err) return reject(err);
assert.equal(null, err);
var db = client.db('NLUAnalysisDB');
insertDoc(db, data).then(obj => {
console.log(obj);
client.close();
return resolve(data._id);
});
});
});
}
Also you need to understand that your insertDoc return Promise and do not accept callback you tried to pass.
Ref: async function
result = insertDoc(db, data).then((data) => {
console.log(data);
}).catch(err => console.error(err));
After searching through countless posts I don't understand why I'm still getting a Promise pending after my awaits. The code below should explain it but I'm trying to pull a MongoDB query of the max value of a column/schema. The console.log within the function is giving me the correct timestamp but I'm trying to pass that out of the inner scope and function to another function.
This is pure NodeJS with only MongoDB imported. Can this be done without any external packages?
export async function getMaxDate() {
var time = MongoClient.connect(url, { useUnifiedTopology: true }, function (err, db) {
if (err)
throw err;
var dbo = db.db(getDB);
dbo.collection(getColl)
.find()
.limit(1)
.sort({ 'timestamp': -1 })
.toArray(function (err, result) {
if (err)
throw err;
time = result[0].time; // THIS IS GIVING THE CORRECT VALUE
console.log(time)
db.close();
});
});
return time
}
export async function getMax() {
var block = await getMaxDate();
return block
}
var t = getMax();
console.log(t); // THIS IS GIVE ME A PROMISE PENDING
getMax() returns a promise, you have to wait for it.
var t = await getMax()
Also getMaxDate uses an async callback that you want to promisify:
export async function getMaxDate() {
return new Promise((resolve,reject) => {
MongoClient.connect(url, { useUnifiedTopology: true }, function (err, db) {
if (err)
return reject(err);
var dbo = db.db(getDB);
dbo.collection(getColl)
.find()
.limit(1)
.sort({ 'timestamp': -1 })
.toArray(function (err, result) {
if(err)
reject(err);
else {
let time = result[0].time; // THIS IS GIVING THE CORRECT VALUE
console.log(time)
db.close();
resolve(time);
}
});
})
});
}
For reference, A is the same thing as B here:
async function A(x) {
if(x)
throw new Error('foo');
else
return 'bar';
}
function B(x) {
return new Promise((resolve,reject)=>{
if(x)
reject(new Error('foo'));
else
resolve('bar');
});
}
Promises came first, then async/await notation was introduced to make common Promise coding practices easier
You can use A and B interchangeably:
async function example1() {
try {
await A(1);
await B(0);
}
catch(err) {
console.log('got error from A');
}
}
async function example2() {
return A(0).then(()=>B(1)).catch((err)=>{
console.log('got error from B');
})
}
I am rewriting an ASPX application using Node. Part of this application is pulling data from a table in SQL Server. I am doing this using async functions to try and force the data to be pulled prior to continuing on. It is essential that I have the data before the next step. However, when it returns from the await the response is null.
const getUserGroups = async (user) => {
let qry = `Select role from [xxxxx] where LOWER(yyyyy)='${user.toLowerCase()}'`;
let groups = await dataQuery.Select(qry, dbList.MCAIntranet)
console.log("Groups: ", groups); //This is undefined
return groups;
// })
}
Here is the Select function:
const Select = async (query, database) => {
// console.log(config[database]
sql.connect(config[database], (err) => {
if (err) console.log(err);
let request = new sql.Request();
request.query(query, (err, recordset) => {
console.log(recordset)//displays correct data
if (err) {
console.log(err);
return null
}
return recordset;
})
});
}
I am using the mssql module, but since I am fairly new to Node I am uncertain where my mistake lies.
Thanks in advance for looking at this.
In you current code, you don't return anything from your Select function. Espectially you are not wrapping your sql code in promise.
You say, you are using the mssql package. This already supports async/await, so you should use the apropriate async calls instead of callbacks.
const Select = async (query, database) => {
try {
await sql.connect(config[database]);
let request = new sql.Request();
let recordset = await request.query(query);
return recordset;
} catch (e) {
console.log(e);
return null;
}
}
Furthermore you should read about parameterized queries, because currently your code is quite unsafe, ie it is vulnerable to SQL injections and invalid syntax errors.
You can either wrap your implementation with a Promise object, or return Promise.resolve(data);, and return Promise.reject(err):
const Select = async (query, database) => {
return new Promise((resolve, reject) => {
// console.log(config[database]
sql.connect(config[database], (err) => {
if (err) reject(err);
let request = new sql.Request();
request.query(query, (err, recordset) => {
console.log(recordset)//displays correct data
if (err) {
console.log(err);
reject(err);
}
resolve(recordset);
})
});
});
}
Here's my code
function getprefix(guildid) {
con.connect(function(err) {
con.query('SELECT * from ' + configcontent.guilds_table_name + ' WHERE guild_id=' + String(guildid), function(err, result) {
if (err) {
throw err;
} else {
return result[0].prefix;
}
});
});
}
I wanted to make this function to return one value from one column (from first result), but it returns undefined. When i tried to write it on screen (using console.log(result[0].prefix);) it works.
Please help. Sorry for bad description or english. Its my first question on StackOverflow
Your function is asynchronous, so it immediately returns result, now it returns undefined. Value from DB returned later, and passed to callback. You need to use promises(preferred)/callbacks to return async results from your function, something like this:
function getprefix(guildid) {
return new Promise((resolve, reject) => {
con.connect(function (err) {
con.query('SELECT * from ' + configcontent.guilds_table_name + ' WHERE guild_id=' + String(guildid), function (err, result) {
if (err) {
// throw err;
// instead of throwing we passing error to result promise
reject(err)
} else {
// return result[0].prefix;
// instead of return we pass result to promise
resolve(result[0].prefix)
}
});
});
}
}
so usage will look like:
getprefix(guildid)
.then(prefix => console.log(prefix))
.catch(e => console.err('Unable to get prefix', e))
it would be great to check the different ways to handle async programming in JS with callbacks/promises/async awaits in details.
You can't return value from a callback.
Before you make your query, connect to the db. Don't use the callback function for that, cuz making a query supposed to be made with con.query
function getprefix(guildid) {
con.connect();
con.query('SELECT * from ' + configcontent.guilds_table_name + ' WHERE guild_id=' + String(guildid), function(err, result) {
if (err) {
throw err;
} else {
// do stuff with 'results'
}
});
EDIT
You can use async/await like this to get access to the return value, but you have to wrap your code in an IIFE (Immediately Invoked Function Expression) or use top level await, but it's only available in the latest version of Node.
Note, that I only used IIFE, because I don't have any other function or logic where I could call my query. If you call it in another function, make it async.
// Top level action
(async () => {
const output = await makeQuery();
console.log(output);
})();
// Query
function makeQuery() {
return new Promise((resolve, reject) =>
db.query("SELECT * FROM users", (err, res) => {
if (err) reject(err);
else resolve(res);
})
);
}
I create a promise function to processing a long-time query task. Some time the task will block for hours. I want set a time out to stop the task. Below is the code.
It can return error message correctly, but it still running connection.execute() for long time before stop. So how can stop it immediately when it return reject message?
Thanks!
function executeQuery(connection, query) {
return new Promise((resolve, reject) => {
"use strict";
//long time query
connection.execute(query, function (err, results) {
if (err) reject('Error when fetch data');
else resolve(results);
clearTimeout(t);
});
let t = setTimeout(function () {
reject('Time Out');
}, 10);
})
(async () => {
"use strict";
oracle.outFormat = oracle.OBJECT;
try {
let query = fs.readFileSync("query.sql").toString();
let results = await executeQuery(connection, query);
console.log(results.rows);
} catch (e) {
console.log(`error:${e}`);
}
So how can stop it immediately when it return reject message?
According to the docs, you can use connection.break:
return new Promise((resolve, reject) => {
connection.execute(query, (err, results) => {
if (err) reject(err);
else resolve(results);
clearTimeout(t);
});
const t = setTimeout(() => {
connection.break(reject); // is supposed to call the execute callback with an error
}, 10);
})
Make sure to also release the connection in a finally block.
Try this (using bluebird promises):
var execute = Promise.promisify(connection.execute);
function executeQuery(connection, query) {
return execute.call(connection, query)
.timeout(10000)
.then(function (results) {
// handle results here
})
.catch(Promise.TimeoutError, function (err) {
// handle timeout error here
});
.catch(function (err) {
// handle other errors here
});
};
If this still blocks, there's a possibility that the database driver you are using is actually synchronous rather than asynchronous. In that case, that driver would be incompatible with the node event loop and you may want to look into another one.
As Bergi mentioned, you'll need to use the connection.break method.
Given the following function:
create or replace function wait_for_seconds(
p_seconds in number
)
return number
is
begin
dbms_lock.sleep(p_seconds);
return 1;
end;
Here's an example of its use:
const oracledb = require('oracledb');
const config = require('./dbConfig.js');
let conn;
let err;
let timeout;
oracledb.getConnection(config)
.then((c) => {
conn = c;
timeout = setTimeout(() => {
console.log('Timeout expired, invoking break');
conn.break((err) => {
console.log('Break finished', err);
});
}, 5000);
return conn.execute(
`select wait_for_seconds(10)
from dual`,
[],
{
outFormat: oracledb.OBJECT
}
);
})
.then(result => {
console.log(result.rows);
clearTimeout(timeout);
})
.catch(err => {
console.log('Error in processing', err);
if (/^Error: ORA-01013/.test(err)) {
console.log('The error was related to the timeout');
}
})
.then(() => {
if (conn) { // conn assignment worked, need to close
return conn.close();
}
})
.catch(err => {
console.log('Error during close', err)
});
Keep in mind that the setTimeout call is just before the execute (because of the return statement). That timeout will start counting down immediately. However, the execute call isn't guaranteed to start immediately as it uses a thread from the thread pool and it may have to wait till one is available. Just something to keep in mind...