jq.run('.', '/path/to/file.json').then(console.log) is asynchronous so when I try to use it I get this: Promise { <pending> } AND THEN I get the result, but it's too late... so how can I fix this ?
I try to wait with await but I don't know where I can put this keyword. So here's my code:
const jq = require('node-jq')
const filter = '[.root[].A[].AT]'
const jsonPath = './simple.json'
data = jq.run(filter, jsonPath).then((output) => {
console.log(output)
}).catch((err) => {
console.error(err)
})
fs.appendFile('./jqTest.txt', data + "\r\n", function (err) {
if (err) throw err;
console.log("complete!")
});
The whole point of asynchronous APIs is that you can't write
data = getResultsAsynchronously();
doStuffWith(data);
...
(Unless you use await, which is slightly magical.)
Instead, traditional asynchronous APIs take a function to call when the result is ready:
getResultsAsynchronously(function (data) {
doStuffWith(data);
...
});
I.e. all the code that follows the original function call in the synchronous version is instead put into a callback function and passed into getResultsAsynchronously.
Promises still follow this general pattern, but let you decouple starting the asynchronous operation itself from deciding how to handle the result. That is, you can start an asynchronous operation first and register a callback that handles the results later, in a second step:
promise = getResultsAsynchronously();
// and later:
promise.then(function (data) {
doStuffWith(data);
...
});
However, you don't have to separate the two steps if you don't want to:
getResultsAsynchronously().then(function (data) {
doStuffWith(data);
...
});
.then also returns a promise, to which you can attach further callbacks by calling .then or .catch.
In your code,
data = jq.run(filter, jsonPath).then(...).catch(...)
data is just another promise, but one without any useful return value inside (because your then and catch callbacks don't return any value).
To fix your logic, it should look like this:
jq.run(filter, jsonPath).then((data) => {
fs.appendFile('./jqTest.txt', data + "\r\n", (err) => {
if (err) throw err;
console.log("complete!")
});
}).catch((err) => {
console.error(err)
});
To recap: Asynchronous results are only available inside callback functions. You can't use the return value like with a synchronous operation.
That said, async / await let you convert asynchronous code into synchronous code (or at least something that looks synchronous). However, this trick only works "on the inside": The external interface is still asynchronous, you can just write more normal looking code internally.
For example:
// await is only available inside async functions, so let's define one:
(async function () {
// magic happens here:
let data = await jq.run(filter, jsonPath);
fs.appendFile('./jqTest.txt', data + "\r\n", (err) => {
if (err) throw err;
console.log("complete!")
});
})(); // ... and invoke it immediately
Internally, JavaScript rewrites
x = await f();
doStuffWith(x);
...
into something that looks like
return f().then((x) => {
doStuffWith(x);
...
});
i.e. await lets you pull the contents of a callback function out into straight line code. Ultimately the whole async function still returns a promise, however.
Related
I have the following JS code
async function readAtlasAll() {
return await MongoClient.connect(url, async function(err, db) {
var dbo = db.db("Users");
c = dbo.collection("List");
r = await c.find({}).toArray();
console.log(r);
return r;
});
}
console.log(readAtlasAll());
I'm not sure why, but printing the result of readAtlasAll() comes before printing the variable r inside the function, even though I'm awaiting the result of r beforehand. The terminal prints Promise { <pending> } first and afterwards prints the contents of r.
I'm relatively new to JavaScript, so I would really appreciate the help. Thanks!
You cannot use both await and a plain callback on MongoDB API calls as the await won't do anything useful. Use one or the other, not both. This is because the MongoDB asynchronous APIs will return a promise if you do NOT pass a callback to them and you can then await that promise, but if you pass a callback, they do not return a promise, therefore the await does nothing useful at all. Here's how you would implement it by leaving out the callback and using await:
async function readAtlasAll() {
const db = await MongoClient.connect(url);
const dbo = db.db("Users");
const c = dbo.collection("List");
const r = await c.find({}).toArray();
return r; // this will be the resolved value of the returned promise
}
readAtlasAll().then(r => {
console.log(r);
}).catch(err => {
console.log(err);
});
Keep in mind that await has no magic powers at all. ALL it does is suspend execution of the function until a promise resolves/rejects. So, it only does anything useful when you await a promise.
And, further, ALL async functions return a promise so there is NO way to return an asynchronously-retrieved value directly from your function. You have to use an asynchronous mechanism for communicating back an asynchronously retrieved value such as a promise, a callback or an event. So readAtlasAll() can never return your value directly.
See How to return the response from an asynchronous call for more info on that.
I'm new to promises, so apologize for the newbie question. Inside my function, I have the following lines (middle of the function):
...
const retPromise = await buildImgs(file, imgArray);
retPromise.then(async function () {
console.log("completed build imgs");
...
My assumption was that the "then" from the promise would not execute until the await was completed. but alas, it is acting like sync code, and the retPromise evaluating the "then" before the buildImgs is completed (as measured by my console.log flows). The result is an undefined retPromise.
please help...what am I missing in the concept?
OK: after feedback, let me explaing further my question:
I am trying to understand this async/sync flow and control concept:
const retVal = somefunc();
console.log(retVal);
const retVal = await somefunc();
console.log(retVal);
in the first case, if I understand sync / async code correctly, then I should have a possibility that retVal is undefined when the console.log finds it...
in the second case, I thought it would stop flow until that point that somefunc() completes, then the flow would continue. However my reading seems to indicate it will still try to run the console.log message as a parallel thread. So this leads me to believe I would need to put the console.log inside of the .then after somefunc. Which leads me to promises. So I made a promise return, which I see happening.
However, the .then, as in my original post code, seems to post the console message "completed build imgs", before code inside my buildImgs completes (measured by time I know the function to take, and also console messages inside the buildImgs to help me with sequencing)
so it seems to me I am still missing a fundamental on how to block flow for async code. :(
When you use await construction the script waits until the promise resolves and return to your retPromise value from this promise.
So in this case better to choose one. Remove await and keep then, or keep await and use retPromise value.
Assuming that buildImgs is actually returning a promise (example)
const buildImgs = (file, imgArray) => {
return new Promise((resolve, reject) => {
try {
// ...
resolve()
} catch (err) {
reject(err)
}
})
}
When you call await on a promise its already waiting for the promise to complete, if you remove the await on the call then you can use .then
there are two ways to write promise handlers, the older way looks like this
buildImgs(file, imgArray)
.then(() => {
console.log('im done')
})
.catch(err => {
console.error(err)
})
and the newer syntax
// you must declare "async" in order to use "await"
const asyncFunction = async () => {
try {
await buildImgs(file, imgArray)
console.log('im done')
} catch(err) {
console.error(err)
}
}
asyncFunction()
if you need the return value from the promise, then you would assign it to a variable
const ineedResponse = await buildImgs(file, imgArray)
console.log(ineedResponse)
// or
buildImgs(file, imgArray)
.then(ineedResponse => {
console.log(ineedResponse)
})
.catch(err => {
console.error(err)
})
I am trying to build an API through which I can get whois detail in the JSON output like below
For this, I installed the whois package from npm (https://www.npmjs.com/package/whois[whois Link]2). I tried to convert the string to object and print it in JSON format but I am not getting any output on the web but it console i can get the data easily. Can you guys please fix my error.
function whoisFunction() {
var whois = require('whois')
whois.lookup(url,async function(err, data) {
try {
a = await data.split('\n')
}
catch (e) {
console.log(e)
return e
}
c=[]
for(i = 0; i < a.length; i++){
c.push(a[i])
}
await console.log(typeof (c))
console.log(c)
return a
})
}
// res.json({'Headers':+whoisFunction()})
res.status(200).json(whoisFunction())
There are async and awaits sprinkled seemingly randomly all over your function.
You should realize that the only thing that is asynchronous here is whois.lookup().
console.log is not asynchronous. Array.prototype.split is not asynchronous. The callback (err, data) => {...} is not asynchronous.
If you want to use the callback pattern, then you need to use res.send() inside of the callback
(err, data) => {
res.send(data)
}
But we got fed up with callback-patterns because of how messy it is to nest them. So instead we went over to using promises. If you have callbacks but want use promises, then you wrap the callback in a promise. You do it once, and you do it as tight to the offending callback as you can:
function promisifiedLookup(url){
return new Promise( (resolve, reject) => {
whois.lookup(url, function(err, data) {
if(err) reject(err)
else resolve(data)
})
})
}
So, to use async/await we need that:
the calling function is declared async
the called function is returning a promise (or else there is nothing to wait for)
async function whoisFunction() {
let data = await promisifiedLookup(url) // _NOW_ we can use await
data = data.split('\n')
// ...
return data; // Because this funtion is declared async, it will automatically return promise.
}
If your express-handler is defined as async, then you now can use await here as well:
res.status(200).json(await whoisFunction())
I am trying to use a promise to call a function getTweets.
Not using an AJAX call, but a simple promise 'call' from 1 javascript file to another.
The function works, but i keep getting 'undefined'.
I have read dozens of questions here on stackoverflow and have spent days
to understand promises, but still can't solve it.
var Twit = require('twit') // Imports the Twitter library
require('dotenv').config() // to get the environment vars into the app
// This is the function:
function getTweets (screen_name) {
let T = new Twit({ /* <twitter key and token here> */ });
T.get('statuses/user_timeline', { screen_name: screen_name, count: 3}, function (err, data, response) {
let myTweets = [];
for (let i in data) {
let text = data[i].text;
myTweets.push(text);
}
return myTweets;
})
}
module.exports.getTweets = getTweets;
And this is the promise that tries to get the tweets:
var promise = tweets.getTweets('dizid');
promise.then(
console.log(myTweets),
console.log(err))
// gives error: promise.then(
// ^
// TypeError: Cannot read property 'then' of undefined
Any help greatly appreciated.
Your problem is that you never return anything from your getTweets() function even though it needs to return a promise. The function calls T.get() and pass it a callback function. You return from this callback function but this doesn't do anything, it doesn't mean that this value gets returned from getTweets().
This is a pretty common mistake when it comes to working with asynchronous calls. What needs to be done is to make getTweets() return a promise that gets resolved when it should.
When working with asynchronous calls that don't implement the promise interface, you need to wrap this call with a new promise. Your getTweets() function should then look like this:
function getTweets (screen_name) {
let T = new Twit({ /* <twitter key and token here> */ });
return new Promise(function(resolve, reject) {
T.get('statuses/user_timeline', { screen_name: screen_name, count: 3}, function (err, data, response) {
if (err) {
reject(err); // Reject the promise since there was an error
} else {
let myTweets = [];
for (let i in data) {
let text = data[i].text;
myTweets.push(text);
}
resolve(myTweets); // Resolve the promise with the result
}
});
});
}
However, it seems the Twit API does support the promise interface, so instead of providing a callback function you can just use the promise created by T.get(). HMR's answer explains how to do this.
Another mistake you've made is with this code:
promise.then(
console.log(myTweets),
console.log(err))
The way you've written it, it reads "Run console.log(myTweets) and console.log(err), then invoke promise.then() with the result of the former as the first argument and the result of the latter as the second argument.
then() takes callback functions (which get invoked depending on the resolving/rejection of the promise) as arguments, so the code should look like this:
promise.then(
function(myTweets) {
console.log(myTweets);
},
function(err) {
console.log(err);
});
Async/await
If you're interested in taking things further, the modern approach to working with asynchronous code is async/await, which is syntactic sugar for promises that lets you write asynchronous code more similar to regular synchronous code.
A function marked as async will implicitly return a promise, but you write it as if you return a regular value. Using the await keyword inside an async function will implicitly wait for a promise to resolve and unwrap the resolved value. The main practical benefits of this is that you can use asynchronous calls in loops and handle errors with regular try-catch blocks. Your getTweets() function would look like this using async/await:
async function getTweets(screen_name) {
let T = new Twit({ /* <twitter key and token here> */ });
const data = await T.get('statuses/user_timeline', { screen_name: screen_name, count: 3});
// Let's also use map() instead of a for loop
let myTweets = data.map(function(item) { return item.text; });
return myTweets;
}
Since get seems to return a promise you don't need to use a callback. Get Tweets can look something like this:
// in getTweets
return T.get(
'statuses/user_timeline',
{ screen_name: screen_name, count: 3}
).then(
function (data) {
console.log("data:",JSON.stringify(data,undefined,2));
return data.map(item=>item.text);
}
)
// exports the function getTweets so that other modules can use it
module.exports.getTweets = getTweets;
If that didn't work please let us know what the output of the program is (update question).
You can call getTweets like so:
tweets.getTweets('dizid')
.then(
myTweets=>
console.log(myTweets),
err=>
console.log(err)
)
I think you forget add function like
promise.then(function(res){
//code
}
Your .then() should include a call back function.
promise.then( res => {
console.log(res);
});
edit: I'm using an ES6 syntax for arrow functions, in case you're new to that.
I have this little code snippet:
var okidok = parseString(myXml, (err, result) => {
console.log(result.rss.channel[0].item); //Gives the output that I want.
return result.rss.channel[0].item;
});
console.log(okidok); //Output is rubbish.
Why okidok is not assigned value which return from parseString. All of this is happening inside an async function.
If anyone can enlighten me, I would be very greatful!
If this is happening inside an ES7 async function you can wait parseString function to resolve doing this :
First you'll need to promisify parseString (in case it's not already thenable) :
function parseStringPromise(text){
return new Promise((resolve,reject) => {
parseString(text, (err, result) => {
if(err) return reject(err);
return resolve(data);
});
});
}
Then you can await for this function to get the result outside of its scope in an async function :
async function parseXml(myXml) {
const result = await parseStringPromise(myXml);
var okidok = result.rss.channel[0].item;
}
Note that you can add a try/catch statement to handle errors in async function.
Edit : I forgot to tell you why.
You don't have the same result because the parseString function is a Node style callback ("nodeback") asynchronous function. Asynchronous function means that you have to wait for it to resolve in order to get the result. There's some different types of asynchronous functions, the main 2 are the callback based (like your parseString) and the promises (resolvable by .then(fn) or await in async functions).
In order to use await on a function this function needs to return a promise (that's why we promisified parseString)
Here's a promise tutorial easier to understand : https://scotch.io/tutorials/javascript-promises-for-dummies