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.
Related
I am new to typescript / javascript, so I don't know much about promises. Here is my use-case: I am creating three different promises inside my cloud-function and then returning it with Promise.all([promise1, promise2, promise3]). Each of these promises are created inside a function with "return Promise...".
My question is, when I add ".catch" inside these functions, will Promise.all still work?. Does it make any difference returning someServiceThatCreatesPromise() with and without .catch()?
Code
export async function myCloudFirestoreFunction() {
try {
const myFirstPromise = createFirstPromise()
const mySecondPromise = createSecondPromise()
const thirdPromise = createThirdPromise()
return Promise.all([
myFirstPromise,
mySecondPromise,
myThirdPromise
]);
} catch (err) {
functions.logger.err(`Something bad happened, See: ${(err as Error).message}`
}
}
// Difference with and without `.catch`?
function createFirstPromise() {
return someServiceThatCreatesPromise().catch((err) => { // LOGGING });
}
// Difference with and without `.catch`?
function createSecondPromise() {
return someServiceThatCreatesPromise().catch((err) => { // LOGGING });
}
// Difference with and without `.catch`?
function createThirdPromise() {
return someServiceThatCreatesPromise().catch((err) => { // LOGGING });
}
Adding .catch inside createNPromise won't affect anything assuming all your Promises resolve and do not reject.
However, if one of the Promises rejects and you catch it within the .catch method, then it won't work as you're expecting unless you re-throw that error and catch it again inside the try/catch in your myCloudFirestoreFunction function.
async function myCloudFirestoreFunction() {
try {
const result = await Promise.all([
createFirstPromise(),
createSecondPromise()
]);
} catch (error) {
console.log({ error });
}
}
function createFirstPromise() {
return Promise.reject("Oof").catch((e) => {
// do work, e.g. log, then
// pass the error forward so that it can be caught
// inside the caller
throw e;
});
}
function createSecondPromise() {
return Promise.resolve("value");
}
myCloudFirestoreFunction();
Alternatively, you just catch errors inside the caller (myCloudFirestoreFunction) instead of catching them separately.
async function myCloudFirestoreFunction() {
const result = await Promise.all([
createFirstPromise(),
createSecondPromise()
]).catch((err) => console.log({ err }));
}
function createFirstPromise() {
return Promise.reject("Oof");
}
function createSecondPromise() {
return Promise.resolve("value");
}
myCloudFirestoreFunction();
when I add ".catch" inside these functions, will Promise.all still work?
Calling catch() on a promise does not in any way change the way the original promise works. It is just attaching a callback that gets invoked when the first promise becomes rejected, and also returning another promise that resolves after the original promise is fulfilled or rejected.
Does it make any difference returning someServiceThatCreatesPromise() with and without .catch()?
It would not make any difference to the code that depends on the returned promise. Both the original promise and the one returned by catch() will tell downstream code when the original work is done by becoming fulfilled.
I suggest reading comprehensive documentation on promises, for example:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
There is a diagram in there that you can follow to understand what happens at each turn. Also in that document, you will read:
Processing continues to the next link of the chain even when a .then() lacks a callback function that returns a Promise object. Therefore, a chain can safely omit every rejection callback function until the final .catch().
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)
})
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.
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
});
}
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();
});
})