It's my code:
const mariadb = require('mariadb');
var test = async function(){
var ret = "";
await mariadb.createConnection({
host: "localhost",
user: "dave",
connectionLimit: 5,
password: "!##",
database: "db",
rowsAsArray: false
}).then((data)=>{
ret = "test";
});
return ret;
}
console.log(test());
How can I get a return from then using await?
I know that similar questions have been raised in many cases, but I could not find any using mariadb.
Already I can see a few pitfalls, which as beginners you will come across in this code:
avait is waiting for results to be returned, as he will get Promisses to solve it
then solves promises asynchronously
function defined as async always returns promises
After a moment of delight, 'async / await' now I try to avoid him. Mainly because every function in which I use the asynchronous function must also be asynchronous until you use then, eg make small test:
let test = async function() {
let x = await 11
return 1;
}
console.log(test()) //returns: Promise { <pending> }
There is nothing asynchronous there, but adding async / await has caused a mess.
Now fixes for your code
const mariadb = require('mariadb');
// use local scope `let` instead global `var`
let test = async function(){
let conn = await mariadb.createConnection({
host: "localhost",
user: "dave",
connectionLimit: 5,
password: "!##",
database: "db",
rowsAsArray: false
});
return conn.query("SELECT 1 as val") // no sense using `avait` because `test()` returns `promise`
}
test().then(function(rows) { console.log(rows)});
and without asnc. then can return promise and this can be resolbed by next then
mariadb.createConnection(...).then(conn => { // create connection
return conn.query("SELECT 1 as val") // make query
}).then(rows => { //get result
console.log(rows)
}).catch(err => console.error(err)) // get errors
By the way: get interested in query builder, for example knex.js. It allow write code independent from database engine.
Update
Let's start with the fact that Node is based on events.
Let's take an example of receiving data from the database. In PHP / C ++, you make an query, wait, receive a result. And this behavior simulates by the await. (Await appeared somewhere near version 8)
Normally, the code in the Node works so that you execute the query, and Node creates a new thread. Old run next instruction, in new you will get the results. (OK, I'm lying, but it's easier to explain).
So, you have to handle the event of receiving the data. And more specifically the promise of providing data. By await,.then (), orcallback (hell)
first code explanation:
You try return ret, but this code first make return ret and after make assigment.
await "returns" data, so you should use let var_name = await asyncFunction()
I'm guess, that you want this:
let getSomeDataFromDB = function(){
return mariadb.createConnection([skip]).then(conn => {
return conn.query("SELECT 1 as val")
})
}
let rows = await getSomeDataFromDB()
In this function you return promise, who return promise. And by await this promise chain is resolved.
But here is a "small" error in the code. Because you are connecting and you are not ending your connection anywhere. Therefore, it is better to have a global connection object, or use something like this:
let getSomeDataFromDB = function(){
return new Promise(function(resolve, reject){
mariadb.createConnection([skip]).then(conn => {
conn.query("SELECT 1 as val")
.then(rows=>resolve(rows))
.catch(e=>reject(e))
.then(()=>conn.close())
}).catch(e => reject(e))
})
}
let rows = await getSomeDataFromDB()
And here you discover another important thing: resolve does not interrupt the execution of the code. Despite the data being returned to the user, you can still do something.
Or the same with await
let getSomeDataFromDB = async function(){
let conn = await reateConnection([skip])
let rows = await conn.query("SELECT 1 as val")
await conn.close();
return rows;
}
let rows = await getSomeDataFromDB()
Related
I have tried a thousand ways to do this, but none work properly. Here msg1 and msg2 are stand ins for functions that I want to call synchronously (first one then the other). A for loop produces "unknown" in the second loop. After searching, I think this is the right way to do it, but I am still not there. The answer that I would like to get is:
msg1
msg2
but I am getting
msg1
msg1
Here is my code
var data = {
"messages": ["msg1", "msg2"],
"i": -1
}
const increment = async () => {
data.i = await data.i + 1;
};
const test = async () => {
data.messages.forEach((fcn) => {
increment(data)
.then(() => {
console.log(data.messages[data.i])
return data
})
})
};
test(data)
UPDATE
the asynchronous answer from jfriend00 (below) works!!! BUT, when used in my actual code, it runs one extra time producing "undefined" in the final loop. I have tried changing the starting value for i, different formulations of the for loop and adding a check for the value "undefined" (as below), but the undefined value remains goes undetected. I have also lengthened the delay (unsuccessful). How can I ensure that the loop only runs once per message?
if (data.i !== undefined) {
console.log(data.messages[data.i])
}
Since there's nothing asynchronous here, you can just remove all the async, await and .then() and then run your synchronous code (you can run each of these snippets directly, right in the browser to see what they send to the console).
var data = {
"messages": ["msg1", "msg2"],
"i": -1
}
const increment = () => {
++data.i;
};
const test = () => {
data.messages.forEach((fcn) => {
increment(data)
console.log(data.messages[data.i])
});
};
test(data)
If you want the increment() function to be an actual asynchronous operation and you want to sequence the calls to increment() then, you have to ditch .forEach() and use a for loop and use await appropriately and insert something that is actually asynchronous into the increment() function (I inserted a promise-based delay):
function delay(t) {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
var data = {
"messages": ["msg1", "msg2"],
"i": -1
}
const increment = async () => {
await delay(100);
++data.i;
};
const test = async () => {
for (let msg of data.messages) {
await increment(data);
console.log(data.messages[data.i])
}
};
test(data)
Comments
Your existing code is misusing async, await and .then(). There are no asynchronous operations anywhere in your existing code so there's no reason for any of async, await and .then() as that just makes things more complicated than required.
In addition, you only ever use await when you are awaiting a promise. So, data.i = await data.i + 1; is misguided. It will run, but the await doesn't do anything useful - it only makes the code more complicated.
I am new to working with asynchronous functions and I have some questions about how await is supposed to work. I have the following function and I am confused by the output.
arrayA = []
arrayB = []
con.query('SELECT * FROM table', async function(err, result) {
test = await 'test';
console.log(test);
if (err) throw err;
n = 0;
for (var column of Object.keys(result)) {
arrayA[n] = await result[column].A;
arrayB[n] = await result[column].B;
n = n+1;
}
});
console.log(arrayA);
What I expect the output to be is:
test
[
"item 1"
"item 2"
...
"item n"
]
but what I get is:
[]
test
Given this it's clear that it is not waiting for the arrays to be populated before continuing or even for the test variable to be written. I have been looking everywhere reading about how promises and asynchronous functions work but haven't been able to resolve this so any help would be much appreciated.
con.query is asynchronous, hence the callback, so your last console.log(arrayA) is not going to wait for con.query (and the callback within) to finish before executing.
The order of events (in comments) is as follows:
# 1
arrayA = []
# 2
arrayB = []
# 3 (the query is sent to the server, but we do not wait for a response
con.query('SELECT * FROM table', async function(err, result) {
# 5 (await will "pause execution" for the
# expression to the right, a string is not a promise so this doesn't do anything
test = await 'test';
# 6
console.log(test);
# 7
if (err) throw err;
# 8
n = 0;
# 9, 10, etc
for (var column of Object.keys(result)) {
# is result[column].A a promise? if not, await won't do anything.
arrayA[n] = await result[column].A;
arrayB[n] = await result[column].B;
n = n+1;
}
});
# 4 (still waiting on the server to respond, callback is "scheduled")
console.log(arrayA);
What you would need is to await con.query, but since it uses callbacks await won't work, so you would need to wrap in a promise or "promisify":
(async () => {
arrayA = []
arrayB = []
// wait until resolve is called
await new Promise((resolve, reject) => {
con.query("SELECT * FROM table", async function (err, result) {
if (err) return reject(err);
for (var column of Object.keys(result)) {
arrayA[n] = result[column].A;
arrayB[n] = result[column].B;
}
// you can await more things here if you need to
// calling resolve here resolves the promise we are awaiting for con.query
resolve();
});
});
// since we awaited a promise above, con.query should be done
console.log(arrayA);
})().catch((e) => {
console.error(e);
process.exit(1)
});
Using callbacks and promises at the same time is always a bit verbose. Many libraries implement a promise interface so your could could be as simple as the below if the mysql client library you're using supported a Promise interface:
const rows = await con.query("SELECT * FROM table");
rows.forEach(row => { ... });
You are mixing two concepts here. Your SQL execution is using a callback function, which gets invoked once the query is done executing. This is not blocking (this is why you see the empty array printed immediately).
You now have defined the callback function to be async, which means that you can await other async operations in it. This await is then blocking, but only in the context of this execution context (function). Given that a string assignment isn't really an async operation it probably will not behave any different than a normal execution of the function.
There are multiple ways by which you can solve the issue. But this is the most convenient one.
var arrayA = [];
var arrayB = [];
try {
var result = await con.query("SELECT * FROM table");
n = 0;
for (var column of Object.keys(result)) {
arrayA[n] = await result[column].A;
arrayB[n] = await result[column].B;
n = n + 1;
}
console.log(arrayA);
} catch (err) {
console.log(err);
}
You can read more on this topic here
I'm trying to read data from a MongoDB Atlas collection using Node.js. When I try to read the contents of my collection I get the error MongoError: Cannot use a session that has ended. Here is my code
client.connect(err => {
const collection = client
.db("sample_airbnb")
.collection("listingsAndReviews");
const test = collection.find({}).toArray((err, result) => {
if (err) throw err;
});
client.close();
});
I'm able to query for a specific document, but I'm not sure how to return all documents of a collection. I've searched for this error, I can't find much on it. Thanks
In your code, it doesn't wait for the find() to complete its execution and goes on to the client.close() statement. So by the time it tries to read data from the db, the connection has already ended. I faced this same problem and solved it like this:
// connect to your cluster
const client = await MongoClient.connect('yourMongoURL', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
// specify the DB's name
const db = client.db('nameOfYourDB');
// execute find query
const items = await db.collection('items').find({}).toArray();
console.log(items);
// close connection
client.close();
EDIT: this whole thing should be in an async function.
Ran into the same issue when I updated the MongoClient from 3.3.2 to the latest version (3.5.2 as of this writing.) Either install only 3.3.2 version by changing the package.json "mongodb": "3.3.2", or just use async and await wrapper.
If still the issue persists, remove the node_modules and install again.
One option is to use aPromise chain. collection.find({}).toArray() can either receive a callback function or return a promise, so you can chain calls with .then()
collection.find({}).toArray() // returns the 1st promise
.then( items => {
console.log('All items', items);
return collection.find({ name: /^S/ }).toArray(); //return another promise
})
.then( items => {
console.log("All items with field 'name' beginning with 'S'", items);
client.close(); // Last promise in the chain closes the database
);
Of course, this daisy chaining makes the code more synchronous. This is useful when the next call in the chain relates to the previous one, like getting a user id in the first one, then looking up user detail in the next.
Several unrelated queries should be executed in parallel (async) and when all the results are back, dispose of the database connection.
You could do this by tracking each call in an array or counter, for example.
const totalQueries = 3;
let completedQueries = 0;
collection.find({}).toArray()
.then( items => {
console.log('All items', items);
dispose(); // Increments the counter and closes the connection if total reached
})
collection.find({ name: /^S/ }).toArray()
.then( items => {
console.log("All items with field 'name' beginning with 'S'", items);
dispose(); // Increments the counter and closes the connection if total reached
);
collection.find({ age: 55 }).toArray()
.then( items => {
console.log("All items with field 'age' with value '55'", items);
dispose(); // Increments the counter and closes the connection if total reached
);
function dispose(){
if (++completedQueries >= totalQueries){
client.close();
}
}
You have 3 queries. As each one invokes dispose() the counter increments. When they've all invoked dispose(), the last one will also close the connection.
Async/Await should make it even easier, because they unwrap the Promise result from the then function.
async function test(){
const allItems = await collection.find({}).toArray();
const namesBeginningWithS = await collection.find({ name: /^S/ }).toArray();
const fiftyFiveYearOlds = await collection.find({ age: 55 }).toArray();
client.close();
}
test();
Below is an example of how Async/Await can end up making async code behave sequentially and run inefficiently by waiting for one async function to complete before invoking the next one, when the ideal scenario is to invoke them all immediately and only wait until they all are complete.
let counter = 0;
function doSomethingAsync(id, start) {
return new Promise(resolve => {
setTimeout(() => {
counter++;
const stop = new Date();
const runningTime = getSeconds(start, stop);
resolve(`result${id} completed in ${runningTime} seconds`);
}, 2000);
});
}
function getSeconds(start, stop) {
return (stop - start) / 1000;
}
async function test() {
console.log('Awaiting 3 Async calls');
console.log(`Counter before execution: ${counter}`);
const start = new Date();
let callStart = new Date();
const result1 = await doSomethingAsync(1, callStart);
callStart = new Date();
const result2 = await doSomethingAsync(2, callStart);
callStart = new Date();
const result3 = await doSomethingAsync(3, callStart);
const stop = new Date();
console.log(result1, result2, result3);
console.log(`Counter after all ran: ${counter}`);
console.log(`Total time to run: ${getSeconds(start, stop)}`);
}
test();
Note: Awaiting like in the example above makes the calls sequential again. If each takes 2 seconds to run, the function will take 6 seconds to complete.
Combining the best of all worlds, you would want to use Async/Await while running all calls immediately. Fortunately, Promise has a method to do this, so test() can be written like this: -
async function test(){
let [allItems, namesBeginningWithS, fiftyFiveYearOlds] = await Promise.all([
collection.find({}).toArray(),
collection.find({ name: /^S/ }).toArray(),
collection.find({ age: 55 }).toArray()
]);
client.close();
}
Here's a working example to demonstrate the difference in performance: -
let counter = 0;
function doSomethingAsync(id, start) {
return new Promise(resolve => {
setTimeout(() => {
counter++;
const stop = new Date();
const runningTime = getSeconds(start, stop);
resolve(`result${id} completed in ${runningTime} seconds`);
}, 2000);
});
}
function getSeconds(start, stop) {
return (stop - start) / 1000;
}
async function test() {
console.log('Awaiting 3 Async calls');
console.log(`Counter before execution: ${counter}`);
const start = new Date();
const [result1, result2, result3] = await Promise.all([
doSomethingAsync(1, new Date()),
doSomethingAsync(2, new Date()),
doSomethingAsync(3, new Date())
]);
const stop = new Date();
console.log(result1, result2, result3);
console.log(`Counter after all ran: ${counter}`);
console.log(`Total time to run: ${getSeconds(start, stop)}`);
}
test();
other people have touched on this but I just want to highlight that .toArray() is executed asynchronously so you need to make sure that it has finished before closing the session
this won't work
const randomUser = await db.collection('user').aggregate([ { $sample: { size: 1 } } ]);
console.log(randomUser.toArray());
await client.close();
this will
const randomUser = await db.collection('user').aggregate([ { $sample: { size: 1 } } ]).toArray();
console.log(randomUser);
await client.close();
client.connect(err => {
const collection = client
.db("sample_airbnb")
.collection("listingsAndReviews");
const test = collection.find({}).toArray((err, result) => {
if (err) throw err;
client.close();
});
});
I know this question been asked for many times but as I'm still very new i dont quite understand the answers on previous places.
const publicIp = require('public-ip');
// getting ip address
async function GetIpAddress() {
var a = await publicIp.v4();
return await a;
// it will return my public ip as string
}
however ,as its non blocking i understand if i do something like this as below
var a = GetIpAddress();
console.log(a);
it will probably go promise pending. so what i did was
async function hi(){
var a = await GetIpAddress();
console.log(a);
}
hi();
But here's my question. How do i return it back to my variables without making another function to cover it up? please see code below
var zmq = require('zmq'),
requester = zmq.socket('req'),
publicIp = require('public-ip');
const PORT = "3222";
var IP = GetIpAddress();
console.log(`${IP}:${PORT}`);
// IP will return promise pending?? how i do fix this without making another
// function?
requester.connect('tcp://127.0.0.1:5433');
requester.on('message', function (msg) {
// arr = str.split(",");
console.log(msg.toString());
});
requester.send(
`${IP}:${PORT}`
);
async function GetIpAddress() {
var a = await publicIp.v4();
return a
}
As the full code above shown.. var IP will definitely be promise pending as it will take time. therefore how do it get the function return value without making another function?
EDIT: please understand the duplication is entirely not what i want.. I understand that my knowlege on callbacks , promises and async await wasnt strong but from a guy who comes from synchronous language such as Python itself . I'm very curious onto how do i take variable out. as what i research and saw are mostly
promises within promises or callbacks within callbacks.
what i want is if i can rereplicate what was shown as per below ( python )
def f(x):
return x+1
var1 = 5
var1 = f(var1)
# var1 is now 6
which is bringing callbacks / promises / async await variable out of its own function rather than
var IP = GetIpAddress().then((IP) => {
requester.connect('tcp://127.0.0.1:5433');
console.log('Worker connected to port 5433');
requester.on('message', function (msg) {
// arr = str.split(",");
console.log(msg.toString());
});
requester.send(
`${IP}:${PORT}`
);
console.log(`${IP}:${PORT}`);
});
Change:
var IP = GetIpAddress();
console.log(`${IP}:${PORT}`);
to:
GetIpAddress().then((IP) => {
console.log(`${IP}:${PORT}`);
}
);
You can read 6 Reasons Why JavaScript’s Async/Await Blows Promises Away (Tutorial)
Update:
Option 1 put everything in the then:
GetIpAddress().then((IP) => {
requester.connect('tcp://127.0.0.1:5433');
console.log('Worker connected to port 5433');
requester.on('message', function (msg) {
// arr = str.split(",");
console.log(msg.toString());
});
requester.send(
`${IP}:${PORT}`
);
console.log(`${IP}:${PORT}`);
});
Option 2 is to wrap everything in an async function:
async function hi(){
var a = await GetIpAddress();
console.log(a);
}
hi();
Option 2 is the one I would opt for as it provides a cleaner way of writing things.
you need to add await before the method call. the main method also need to async.
var IP = await GetIpAddress();
try to use
new promise(function (accept, reject){ // if Ip found
accept("ipaddress") //else reject("error log")
}).then(function(response){ // this is accept , print IPaddress
}).catch(function(err){ // this is reject. } );
Note - Message variable is not retaining data after calling promisified functions. Callback is giving null array.
Code -
'use strict';
const Promise = require('bluebird');
let _connectResolve, _connectReject, onConnected = new Promise((resolve, reject) => {
_connectResolve = resolve;
_connectReject = reject;
}),
redis = require("redis"),
redisClient = redis.createClient({
host: 'localhost',
port: 6379
});
Promise.promisifyAll(redis.RedisClient.prototype);
redisClient.on('connect', _connectResolve);
const results = Promise.all([
'it/0I0g2I3D312s192u0U3k/10es.zip',
'items/25210B0c0Q1L3u0X462g/10ges.zip',
'items/2x0n440V1A1n3x1y0f1K/Fs.zip',
'items/2l023931u0w1S2a3j/es.zip',
'items/2O2x212i3t0B2h/es.zip',
]);
var message = [];
var a = Promise.promisify(function(callback) {
results.map(function(result) {
redisClient.getAsync(result).then(function(reply) {
if (reply == null) {
message.push({
"key": result,
"bucket_name": 'dsdds'
});
}
//console.log(message);
});
callback(null, message);
});
});
onConnected.then(() => {
Promise.resolve(a()).then(function(message) {
console.log(message);
});
});
Output - message is undefined
There are quite a few things wrong with how you've coded this. Asynchronous operations run on their own schedule and finish some indeterminate time in the future. As such, you can't do something like use a .map() loop with asynchronous operations in it and then expect the results to be ready right after the .map() loop. Instead, you have to use tools to keep track of when all the async operations in the .map() loop have completed and look at the result only when that tool tells you all the operations are done.
In addition, there are some very weird uses of Promise.promisify() which makes it look like you think promisifying a plain function will somehow magically manage the async operations inside it. It will not. You can only use Promise.promisify() on an async function that has a specific calling convention.
Fortunately, since you have the Bluebird promise library, you can use its tools to help you to do something like this:
function a() {
let message = [];
return Promise.map(results, function(result) {
return redisClient.getAsync(result).then(function(reply) {
if (reply == null) {
message.push({
"key": result,
"bucket_name": 'dsdds'
});
}
});
}).then(function() {
// make the message array be the resolved value of the returned promise
return message;
});
});
onConnected.then(() => {
a().then(function(message) {
console.log(message);
});
});