Testing a promise object which contains a nested callback - javascript

I have the below function which calls a promise object
export const project_list = function(data,res){
return db.any(queries.ProjectList)
.then(function(results){
let newResults = project_list_cleaner(results)
res(null,newResults)
})
.catch(function(err){
res(err)
})
}
I'm trying to test the function like the below
it('should retrieve a list',function(){
return expect(project_list(data,res)).to.eventually.be.false
})
this raised an error because the promise object doesnt actually return anything. it exectes the res callback.
is there anyway to test whether the promise object executes the callback?

The fact that the function above returns a Promise does not matter for it's signature. The fact that you are passing a callback to it makes the Promise thing not usable.
Solution 1: refactor your function
Make your function return a Promise and that's it. No callback involved.
export const project_list = function(data){
return db.any(queries.ProjectList)
.then(function(results){
return project_list_cleaner(results)
});
}
The test will then be:
it('should retrieve a list',function(){
return expect(project_list(data)).to.eventually.be.false
})
Solution 2: ignore the Promise
In this solution you don't refactor the function (but you really should!) and work as Promises never existed.
it('should retrieve a list',function(done){
project_list(data, function(err, result){
// put your assertions here
done();
});
})

Related

About chaining es6 Promises, then() and value consumption

This is tightly coupled to Chaining .then() calls in ES6 promises ...
I tried this with some functions that make up a chain of promises, so basically:
var PromiseGeneratingMethod = function(){
return p = new Promise((resolve, reject) =>{
resolve(1)
});
}
var inBetweenMethod = function(){
return PromiseGeneratingMethod()
.then((resolved) => {
if(resolved){
console.log('resolved in between');
//return resolved
/* this changes output to
resolved in between
resolved at last*/
}else{
console.log('something went terribly wrong in betweeen', resolved);
}
});
}
inBetweenMethod().then((resolved) =>{
if(resolved){
console.log('resolved at last')
}else{
console.log('something went terribly wrong', resolved);
}
})
/* ouput:
resolved in between
something went terribly wrong undefined*/
I don't understand why it is like that. doesn't have a Promise just ONE associated return value? why can I change that value in every then? It seems irrational to me. A Promise Object can only have one return value and I thought every then handler will receive the same parameter after the Promise gets resolved?
This way, having two Methods which call then() on the same Promise, the latter one (in asynchronous environments you never know what that is...) will ALWAYS get an empty result, except if EVERY then returns the desired value
If I got it right, the only good thing is that you can build a then().then().then() chain to make it almost synchronous (by returning arbitrary values in every then()) but you still could achieve the same with nested Promises, right?
Can someone help me understand why es6 Promises work that way and if there are more caveats to using those?
doesn't have a promise just ONE associated return value?
Yes.
why can I change that value in every then?
Because every .then() call does return a new promise.
having two methods which call then() on the same Promise
That's not what you're doing. Your then callbacks are installed on different promises, that's why they get different values.
You could do
function inBetweenMethod() {
var promise = PromiseGeneratingMethod();
promise.then(resolved => { … }); // return value is ignored
return promise;
}
but you should really avoid that. You already noticed that you can get the expected behaviour with
function inBetweenMethod() {
var promise = PromiseGeneratingMethod();
var newPromise = promise.then(value => {
…
return value;
});
return newPromise;
}
where the newPromise is resolved with the value that is returned by the callback - possibly the same value that promise fulfilled with.
you are using .then() handler twice, do the following:
var PromiseGeneratingMethod = function(){
return new Promise((resolve, reject) =>{
if (myCondition) resolve(1)
if (!myCondition) reject("failed")
});
}
var inBetweenMethod = function(){
return PromiseGeneratingMethod()
}
inBetweenMethod().then((resolved) =>{
console.log(resolved)
}).catch(function(err) {
console.log(err)
})

Why do i get undefined, even when using a promise?

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.

AngularJS chain promises sequentially within for loop

How do i chain promises sequentially within for loop, i have seen lot of examples on google to do this but i couldn't implement for my case:
i have gone through this link for sequential chaining of Promises.
What I'm trying to acheive:
Promise1: login();
Promise2: sync();
sync function calls another service complete() for an array of elements. These array of elements must be done sequentially.
ServiceA.login().
then(function(response){
ServiceA.sync()
.then(function(response){
})
})
function sync(){
ServiceB.complete()
.then(function(){
var promises = [];
angular.forEach(response, function (value) {
// The below service call doSomething() must be done sequentially for each "value"
promises.push(doSomething(value));
});
$q.all(promises).then(function () {
});
});
})
}
How do I capture the error occuring in each Promise?
Update:
I have tried the approach suggested by #zaptree with the following code:
ServiceA.login()
.then(function(response){
// you must always return your promise
return ServiceA.sync()
})
// don't nest the .then make them flat like this
.then(function(response){
})
.catch(function(){
// if you made sure to always return your promises this catch will catch any errors throws in your promise chain including errors thrown by doSomething()
});
function sync(){
// you must always return your promise
return ServiceB.complete()
.then(function(){
var result = $q.when();
angular.forEach(response, function (value) {
result = result.then(doSomething(value)); // problem is here that doSomething function is being called before the first call it is resolved
// doSomething is a http call.
});
return result;
})
.then(function(){
// the array of promises has run sequentially and is completed
});
}
function doSomething(data){
return $http({
method: 'POST',
url: '/api/do',
data: data,
headers: {
"Content-Type": "application/json"
}
}).then(function (response) {
}, function (error) {
});
}
If the response in the near the for each loop has 2 values (valuea, valueb) in it, the code is behaving as follows:
1. calling doSomething(valuea)
2. calling doSomething(valueb) before the above promise is resolved.
Expected behaviour:
after the POST method has succesfully completed by the call doSOmething(valuea), then the another POST call should happend i.e., soSomething(valueb).
Here's what I came up with. You'll need to reduce the array into a single promise.
var results = [...];
var sequentialPromise = results.reduce(function(a, b) {
return a.then(function(){
return doSomething(b);
});
}, $q.resolve());
sequentialPromise.then(function(){...});
So here is an example on how you would do the sequential promises with Q, also some improvements on how to do your promises so you can properly catch errors thrown at any point in your promise chain. You must always make sure to return a promise on any method that uses them. Also avoid pyramid code by not nesting the .then to make your code cleaner:
ServiceA.login()
.then(function(response){
// you must always return your promise
return ServiceA.sync()
})
// don't nest the .then make them flat like this
.then(function(response){
})
.catch(function(){
// if you made sure to always return your promises this catch will catch any errors throws in your promise chain including errors thrown by doSomething()
});
function sync(){
// you must always return your promise
return ServiceB.complete()
.then(function(){
var result = $q.when();
angular.forEach(response, function (value) {
result = result.then(doSomething(value));
});
return result;
})
.then(function(){
// the array of promises has run sequentially and is completed
});
}

Can promises be relayed in node

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.

Chain multiple promises in Angularjs

I have the following structure and need to simplify it,
Resuable service1 to interact with IndexedDB
function isDbExist (){
// do something , it return either reject or resolve
}
function createDB (){
// do something , it return either reject or resolve
}
function getData (){
// do something , it return either reject or resolve
}
In another service2 I'm injecting this service1 and using the functions as such
function1 (){
service1.isDbExist.then(function(data){
service1.createDB.then(function(data){
service1.getData.then(function(data){
referred.resolve(data);
},function(error){
deferred.reject(error)
})
},function(error){
deferred.reject(error);
})
},function(error){
deferred.reject(error);
})
}
The problem here is the readability of the code is not good, its not easy to debug as which reject function is for which promise. Is their some good way of doing this ? I have read about $q.all but don't this its applicable in this situation.
Exactly, the beauty of promises is that you can chain them as opposed to nesting them like callbacks. You can return another promise when dealing with the previous like so:
isDbExists().then(function(db) {
// do something
return createDb();
}).then(function(db) {
// do something with with the resolved db..
return getData();
}).then(function(data) {
// Do something with data
}).catch(function(error) {
// Uh oh, something went wrong. Deal with error
});
Finally, you deal with the error that may have occurred.
You can chain multiple promises like this:
service1.isDbExist()
.then(service1.createDB)
.then(service1.getData)
.then(function() {
// DB exists, created and data loaded
})
.catch(function(err) {
console.log('Handle error', err); // isDbExist, createDB or getData rejected
});
Check interactive demo to see how success and error data is passed to the next promise in chain and how you have full control over process on each step:
Demo: http://plnkr.co/edit/PM06e8NGJvHKmoJ9C2Lf?p=info
One of the really great things about promises is that they can be chained like this:
function1 (){
return service1.isDbExist.then(function(exists){
if(!exists) return service1.createDB()
})
.then(function() {
return service1.getData('some', 'args')
});
}
Remember that the .then will receive the result of the previous function, like the exists would be a boolean. function1 will also return a promise and this promise will return the resolved/rejected promise from service1.getData.

Categories