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.
Related
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())
Working case:
async await is working fine when we call a asynchronous function and that function returning a promise resolve()
Not working case:
async await is not working for mongo DB queries
tried then(), async/await
I have 2 JS files.
In one.js file i am importing function which is in functionone.js
WORKING CASE:
When one.js looks like
var functiononestatus = transactions.functionone(req.session.email).then((came) => {
console.log(came); // getting `need to be done at first` message
console.log("exec next")
});
When functionone.js looks like
module.exports.functionone = functionone;
async function functionone(email) {
return await new Promise((resolve, reject) => {
resolve('need to be done at first')
});
});
NOT WORKING CASE (when mongo db query need to be executed):
When one.js looks like
var functiononestatus = transactions.functionone(req.session.email).then((came) => {
console.log(came); // getting undefined
console.log("exec next")
});
When functionone.js looks like
module.exports.functionone = functionone;
async function functionone(email) {
//mongo starts
var collection = await connection.get().collection('allinonestores');
await collection.find({
"email": email
}).toArray(async function(err, wallcheck) {
return await new Promise((resolve, reject) => {
resolve(wallcheck[0])
});
});
Quick clarification:
.collection('name') returns a Collection instance, not a Promise, so no need to await for it.
toArray() operates in two modes: either with a callback when a function is provided, either returns a Promise when no callback function is provided.
You're essentially expecting a Promise result out of toArray() while supplying a callback function, resulting in undefined, because callback takes priority and no promise is returned, due to the dual operation mode of toArray().
Also, toArray(callback) does not take an async function as callback.
Here's how your code should look like, for retrieving a collection:
const client = await MongoClient.connect('your mongodb url');
const db = client.db('your database name'); // No await here, because it returns a Db instance.
const collection = db.collection('allinonestores'); // No await here, because it returns a Collection instance.
and then, code for fetching results:
const db = <get db somehow>;
// You could even ditch the "async" keyword here,
// because you do not do/need any awaits inside the function.
// toArray() without a callback function argument already returns a promise.
async function functionOne(email) {
// Returns a Collection instance, not a Promise, so no need for await.
const collection = db.collection('allinonestore');
// Without a callback, toArray() returns a Promise.
// Because our functionOne is an "async" function, you do not need "await" for the return value.
return collection.find({"email": email}).toArray();
}
and code alternative, using callback:
const db = <get db somehow>;
// You could even ditch the "async" keyword here,
// because you do not do/need any awaits inside the function.
// You're forced to return a new Promise, in order to wrap the callback
// handling inside it
async function functionOne(email) {
// Returns a Collection instance, not a Promise, so no need for await.
const collection = db.collection('allinonestore');
// We need to create the promise outside the callback here.
return new Promise((resolve, reject) => {
db.find({"email": email}).toArray(function toArrayCallback(err, documents) {
if (!err) {
// No error occurred, so we can solve the promise now.
resolve(documents);
} else {
// Failed to execute the find query OR fetching results as array someway failed.
// Reject the promise.
reject(err);
}
});
});
}
Note: First of all i really need to thank #mihai Potra for the best answer.
Here we go
Case 1:
If it is a function which need to find documents and return from MongoDb as mihai mentioned, below answer is uber cool
const db = <get db somehow>;
async function functionOne(email) {
const collection = db.collection('allinonestore');
return collection.find({"email": email}).toArray();
}
case 2:
If there are nested functions which need to return values every time below ans will be the best as of my knowledge
-no need async/await keywords for every function or no need then()
function one(<parameter>) {
return new Promise(function(resolve, reject) {
const collection = connection.get().collection('<collection_name>');
const docs = collection.find({"Key": Value_fromFunction}).toArray( function (err, result) {
resolve(result[0]);
});
That's it, use resolve callback when ever it needed.
I'm using the popular node library, got, to make simple GET requests to a JSON API.
I have a function that abstracts the request, like so:
function performRequest(url) {
got(url, {
json: true
}).then(function (response) {
return formatResponse(response.body);
}).catch(function (error) {
console.log(error.response.body);
});
}
formatResponse is a simple synchronous method that modifies the JSON returned from the API.
I would like to be able to call performRequest from another function and then use the return value (once resolved). Currently, as performRequest is not recognized as an async method, my code is calling it and then proceeding immediately.
function myBigFunction() {
var url = composeUrl();
var res = performRequest(url);
doMoreStuffWithResponse(res);
}
I know that I need to utilize a Promise, however, I'm always unclear as to how to use a Promise in conjunction with a built-in library function that is already using a Promise (like in this case).
I'm also completely open to the possibility that I'm going about this all wrong. In that case, I would appreciate some redirection.
Thank you for your time.
Understand what a Promise is. Its a value, you can treat it as such. In order to "read" the value, you pass a function to the Promise's then method. You don't need myBigFunction. Anything you want to run after the Promise resolves just needs to be passed to then:
var req = performRequest(composeURL());
req.then(doStuffWithResponse);
Now, I don't particularly care for this way although I do it fairly often. I prefer to have functions that take promises and invoke their then method:
var takesAPromise = function(p) {
return p.then(/* does stuff */);
};
Note that it returns the Promise of the completed task. But what I like even better is this ES6 one-liner:
let wrap = f => p => p.then(val => f.call(null, val));
Now you can wrap arbitrary functions to take Promises as input and return them as output. If Promises were a monad, this would be their bind function. Making it work seamlessly with functions of arbitrary arity is left as an exercise to the reader.
You'll always want to return a promise from your functions:
function performRequest(url) {
return got(url, {
//^^^^^^
json: true
}).then(function(response) {
return formatResponse(response.body);
}, function(error) {
throw new Error(error.response.body);
});
}
With this, you can wait for the result in your big functions using another then:
function myBigFunction() {
var url = composeUrl();
var promise = performRequest(url);
return promise.then(function(res) {
return doMoreStuffWithResponse(res);
});
}
or in short
function myBigFunction() {
return performRequest(composeUrl()).then(doMoreStuffWithResponse);
}
so that you can call it like
myBigFunction().catch(function(error) {
console.log(error.message);
});
I have the following requirement,I have three asynchronous function async1(),async2(),ascync3() which all return promises
Now I would call one function and serially execute async1,async2,async3 respectively and I want to print the resolved promise returned after async3
this is my main function
testPromiseSerially = function() {
return new promise(function(resolve,reject) {
async1().
then(function(result1) {
return async2(result1)
})
.then(function(result2){
return async3(result2)
})
.catch(function(err) {
return reject(err)
}
})
}
This is my async3 function
async3 = function(params) {
return new promise(function(resolve,reject) {
return resolve("solved")
})
}
and async1 and async2 are also similar to async3
If I execute this code
testPromiseSerially.then(function(result) {
console.log(result)
})
.catch(function (err) {
console.log(err)
})
testPromiseSerially is getting called but it's not entering 'then' or 'catch'
block.Is promise returned by async3 not relayed back to testpromiseSerially()?
How do I see the result from async3?
I know that if I extend my code like adding
.then(function(result) {
return resolve(result)
})
after async3(result) then I would be able to see the result. But I have chain of functions which depend on promise returned by other functions, so how do I handle that?
The main problem is that your testPromiseSerially code is never calling resolve. So the promise it returns is never resolved.
Since what it's testing already has promises, there's no need for you to create a new one. Every call to then produces a new promise, so just use that.
Additionally, this:
.then(function(result1) {
return async2(result1);
})
is more complicated/verbose than you need, it can be just:
.then(async2)
And the same for the .catch.
So:
let testPromiseSerially = function() {
return async1()
.then(async2)
.then(async3);
};
Example using JavaScript's native promises on Babel's REPL
You should use async https://github.com/caolan/async. It is be better for your case. Look at the waterfall function.
From the documentation
waterfall(tasks, [callback])
Runs the tasks array of functions in series, each passing their results to the next in the array. However, if any of the tasks pass an error to their own callback, the next function is not executed, and the main callback is immediately called with the error.
I'm still struggling with promises, but making some progress thanks to the community here.
I have a simple JS function which queries a Parse database. It's supposed to return the array of results, but obviously due to the asynchronous nature of the query (hence the promises), the function returns before the results, leaving me with an undefined array.
What do I need to do to make this function wait for the result of the promise?
Here's my code:
function resultsByName(name)
{
var Card = Parse.Object.extend("Card");
var query = new Parse.Query(Card);
query.equalTo("name", name.toString());
var resultsArray = [];
var promise = query.find({
success: function(results) {
// results is an array of Parse.Object.
console.log(results);
//resultsArray = results;
return results;
},
error: function(error) {
// error is an instance of Parse.Error.
console.log("Error");
}
});
}
Instead of returning a resultsArray you return a promise for a results array and then then that on the call site - this has the added benefit of the caller knowing the function is performing asynchronous I/O. Coding concurrency in JavaScript is based on that - you might want to read this question to get a broader idea:
function resultsByName(name)
{
var Card = Parse.Object.extend("Card");
var query = new Parse.Query(Card);
query.equalTo("name", name.toString());
var resultsArray = [];
return query.find({});
}
// later
resultsByName("Some Name").then(function(results){
// access results here by chaining to the returned promise
});
You can see more examples of using parse promises with queries in Parse's own blog post about it.
What do I need to do to make this function wait for the result of the promise?
Use async/await (NOT Part of ECMA6, but
available for Chrome, Edge, Firefox and Safari since end of 2017, see canIuse)
MDN
async function waitForPromise() {
// let result = await any Promise, like:
let result = await Promise.resolve('this is a sample promise');
}
Added due to comment:
An async function always returns a Promise, and in TypeScript it would look like:
async function waitForPromise() {
// let result = await any Promise, like:
let result: Promise<string> = await Promise.resolve('this is a sample promise');
}
You're not actually using promises here. Parse lets you use callbacks or promises; your choice.
To use promises, do the following:
query.find().then(function() {
console.log("success!");
}, function() {
console.log("error");
});
Now, to execute stuff after the promise is complete, you can just execute it inside the promise callback inside the then() call. So far this would be exactly the same as regular callbacks.
To actually make good use of promises is when you chain them, like this:
query.find().then(function() {
console.log("success!");
return new Parse.Query(Obj).get("sOmE_oBjEcT");
}, function() {
console.log("error");
}).then(function() {
console.log("success on second callback!");
}, function() {
console.log("error on second callback");
});
You don't want to make the function wait, because JavaScript is intended to be non-blocking.
Rather return the promise at the end of the function, then the calling function can use the promise to get the server response.
var promise = query.find();
return promise;
//Or return query.find();