Promise.all response - javascript

Can I add Promise.resolve(value) and Promise.reject(error) at response of Promise.all().
For example,
function transferFRQ(fromUserId, fromCompanyDetails, toUserDetails, toCompanyDetails,) {
return Promise.all([
transferRFQCompany(fromCompanyDetails, toCompanyDetails),
replaceRFQCreatedBy(fromUserId, toUserDetails)
])
.then(result => Promise.resolve(result))
.catch(error => Promise.reject(error));
}
function transferRFQCompany (fromCompanyDetails, toCompanyDetails) {
return new Promise((resolve, reject) => {
Request.updateMany({
"company.id": fromCompanyDetails._id
}, {
$set: {
company: {
id: toCompanyDetails._id,
name: toCompanyDetails.name,
logo: toCompanyDetails.logo
}
}
}).then(result => resolve(result))
.catch(error => reject(error));
});
}
function replaceRFQCreatedBy (fromUserId, toUserDetails) {
return new Promise((resolve, reject) => {
Request.updateMany({
"createdBy.id": fromUserId
}, {
$set: {
createdBy: {
id: toUserDetails._id,
firstName: toUserDetails.firstMame,
lastName: toUserDetails.lastName
}
}
}).then(result => resolve(result))
.catch(error => reject(error));
});
}
I don't know whether is it correct or not, but what I need is to handle the response of transferRFQ properly because I will need to add transferRFQ in another Promise.all() to handle the error properly.
Am I doing in the wrong way? if so, How to do it correctly
Any additional advice is welcome!

I advice that you should not use unnecessary Promise wrappers, & avoid javascript hoisting.
You should try to do something like this instead
// Note that this is declared before used, to avoid javascript hoisting
function transferRFQCompany (fromCompanyDetails, toCompanyDetails) {
return Request.updateMany({ // updateMany already returns a promise right? no need to wrap it in another promise
"company.id": fromCompanyDetails._id
}, {
$set: {
company: {
id: toCompanyDetails._id,
name: toCompanyDetails.name,
logo: toCompanyDetails.logo
}
}
})
});
}
// Note that this is declared before used, to avoid javascript hoisting
function replaceRFQCreatedBy (fromUserId, toUserDetails) {
return Request.updateMany({ // updateMany already returns a promise right? no need to wrap it in another promise
"createdBy.id": fromUserId
}, {
$set: {
createdBy: {
id: toUserDetails._id,
firstName: toUserDetails.firstMame,
lastName: toUserDetails.lastName
}
}
})
}
function transferFRQ(fromUserId, fromCompanyDetails, toUserDetails, toCompanyDetails,) {
return Promise.all([
transferRFQCompany(fromCompanyDetails, toCompanyDetails),
replaceRFQCreatedBy(fromUserId, toUserDetails)
])
}
// Sample usage async/await style
(async () => {
try {
// put your params, of course
const result = await transferFRQ(...params);
// `result` is result of .then()
} catch (e) {
// `e` is result of .catch()
}
// or use it in promise-style
transferFRQ(...params)
.then(console.log)
.catch(console.error)
})()

Related

How to await promise function inside nested maps?

Suppose I have the following:
async function getReactionScores(userReactions) {
const reactionsWithSharesPromises = userReactions.map((reactions) => {
return {
emoji: reactions.emoji,
shares: reactions.users.map(async (user) => {
return {
username: user.username,
// Just as an example of something to wait for
shares: await sleep(1, user.id),
};
}),
};
});
}
function sleep(time, userID) {
return new Promise((resolve) => setTimeout(resolve(userID), time));
}
How do I await for all the promises inside the second map to resolve? If it was only one map, I could do Promise.all, but promise all is not recursive.
You will need 2 Promise.all
async function getReactionScores(userReactions) {
const reactionsWithSharesPromises = await Promise.all(userReactions.map(async (reactions) => {
return {
emoji: reactions.emoji,
shares: await Promise.all(reactions.users.map(async (user) => {
return {
username: user.username,
// Just as an example of something to wait for
shares: await sleep(1, user.id),
};
})),
};
}));
}

Function returned undefined, expected Promise or value in Cloud Functions

I have a Cloud Function that is triggered when a document is created in Firestore, and I keep getting Function returned undefined, expected Promise or value. The function does what it is supposed to do, but it sometimes takes around 25-30 seconds, so I thought it may have something to do with this error. I would really appreciate if someone could help me understand what to return here. My function is below:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
const Iyzipay = require('iyzipay');
const iyzipay = new Iyzipay({
apiKey: '...',
secretKey: '...',
uri: '...'
});
exports.pay = functions
.region('europe-west1')
.firestore
.document('requests/{docId}')
.onCreate((snap, context) => {
const newValue = snap.data();
const request = {
locale: Iyzipay.LOCALE.TR,
conversationId: newValue.uid,
price: newValue.price,
paidPrice: newValue.price,
currency: Iyzipay.CURRENCY.TRY,
installment: '1',
basketId: 'B67832',
paymentChannel: Iyzipay.PAYMENT_CHANNEL.MOBILE_IOS,
paymentGroup: Iyzipay.PAYMENT_GROUP.LISTING,
paymentCard: {
cardHolderName: newValue.cardHolderName,
cardNumber: newValue.cardNumber,
expireMonth: newValue.expireMonth,
expireYear: newValue.expireYear,
cvc: newValue.cvc
},
buyer: {
id: newValue.uid,
name: newValue.name,
surname: newValue.surname,
gsmNumber: newValue.gsmNumber,
email: newValue.email,
identityNumber: newValue.identityNumber,
registrationAddress: newValue.registrationAddress,
city: newValue.city,
country: newValue.country,
zipCode: newValue.zipCode
},
shippingAddress: {
contactName: newValue.name,
city: newValue.city,
country: newValue.country,
address: newValue.registrationAddress,
zipCode: newValue.zipCode
},
billingAddress: {
contactName: newValue.name,
city: newValue.city,
country: newValue.country,
address: newValue.registrationAddress,
zipCode: newValue.zipCode
},
basketItems: [
{
id: newValue.productid,
name: newValue.productname,
category1: newValue.category1,
itemType: Iyzipay.BASKET_ITEM_TYPE.PHYSICAL,
price: newValue.price
},
]
}
iyzipay.payment.create(request, function (err, result) {
console.log(err, result);
const docRef1 = db.collection('results').doc().set(result);
})
})
You need to terminate a Cloud Function when all the asynchronous work is completed, see the doc. In the case of a background triggered Cloud Function (e.g. Cloud Firestore function onCreate trigger, like your Cloud Function) you must return the chain of Promises returned by the asynchronous method calls.
I don't know the Iyzipay service nor the corresponding Node.js library, but it seems that there is no "promisified" version of the iyzipay.payment.create method. You should therefore wrap it in a Promise and chain this Promise with the Promise returned by the Firestore asynchronous set() method, as follows (untested).
exports.pay = functions
.region('europe-west1')
.firestore
.document('requests/{docId}')
.onCreate((snap, context) => {
const newValue = snap.data();
const request = { ... };
return new Promise(function (resolve, reject) {
iyzipay.payment.create(request, function (err, result) {
if (err) {
reject(err)
} else {
resolve(result)
}
})
})
.then(result => {
return db.collection('results').doc().set(result);
})
.catch(error => {
console.log(error);
return null;
});
});
If you want to write something to the log when the operation is completed, do as follows:
// ...
.onCreate((snap, context) => {
const newValue = snap.data();
const request = { ... };
return new Promise(function (resolve, reject) {
// ...
})
.then(result => {
return db.collection('results').doc().set(result);
})
.then(() => {
console.log("Operation completed: " + result);
return null;
})
.catch(error => {
console.log(error);
return null;
});
Update following your comment:
How can I add another Firestore query after return db.collection('results').doc().set(result);? For example, I want to
update a field in a document, so where can I add
db.collection('listings').doc(newValue.productid).update({sold : true})?
You have two possibilities:
Approach #1
Since the update() method is an asynchronous method which returns a Promise (like all Firebase asynchronous methods), you need to add it to the chain of promises, as follows:
return new Promise(function (resolve, reject) {
iyzipay.payment.create(request, function (err, result) {
if (err) {
reject(err)
} else {
resolve(result)
}
})
})
.then(result => {
return db.collection('results').doc().set(result);
})
.then(() => {
return db.collection('listings').doc(newValue.productid).update({sold: true});
})
.catch(error => {
console.log(error);
return null;
});
Approach #2
Since both the set() the update() methods write to a document, you could use a batched write, as follows:
return new Promise(function (resolve, reject) {
iyzipay.payment.create(request, function (err, result) {
if (err) {
reject(err)
} else {
resolve(result)
}
})
})
.then(result => {
const batch = db.batch();
var docRef1 = db.collection('results').doc();
batch.set(docRef1, result);
var docRef2 = db.collection('listings').doc(newValue.productid);
batch.update(docRef2, {sold: true});
return batch.commit();
})
.catch(error => {
console.log(error);
return null;
});
The difference with Approach #1 is that the two writes are done in one atomic action.
PS: Note that instead of doing db.collection('results').doc().set(result); you could do db.collection('results').add(result);

Reduce on list of promises

I have a list of jobs that should be sequentially executed.
As the jobs take seconds to be finished the should run in the background.
I thought that a job could be described as
interface Job {
name: string
execute(): Promise<boolean>
}
I would like to have a function which takes this list of jobs and execute them sequentially
until the list is completed or one job fails or is rejected, so basically:
function executeUntilFailed(jobs: Job[]): Promise<boolean>
{
// execute first job
// if this job
// - returns with true: continue with the next job
// - returns with false: resolve the promise with false
// - got rejected: reject the promise with the reason prefixed with the jobs name
//
// if there are no more jobs to do, resolve the promise with true
//
// basically it's a reduce operation with starting value of true and
// early stops if one job yields false or got rejected
}
I'm rather new to Javascript/Typescript and have a hard time implementing this.
Thanks,
Dieter
Thanks to Aluan Hadded and ehab.
I collected their solutions and have now the following code,
which does exactly what I need:
interface Job {
name: string
execute(): Promise<boolean>
}
async function executeUntilFailed(jobs: Job[]) {
for (const job of jobs) {
try {
if(!await job.execute()) {
return false
}
}
catch (err) {
throw new Error(`${job.name}: ${err.message}`)
}
}
return true
}
and here is some example for it
class JobImpl implements Job {
constructor(public name: string, private value: boolean, private throwMsg: string|null = null) {}
execute(): Promise<boolean> {
console.log(`executing job '${this.name}'`)
return new Promise((resolve,reject) => {
setTimeout(()=> {
if(this.throwMsg!=null) { reject(this.throwMsg) }
else { console.log(`finished job '${this.name}' with result: ${this.value}`); resolve(this.value) }
}, 1000)
})
}
}
const successJobs = [
new JobImpl("a", true),
new JobImpl("b", true),
new JobImpl("c", true),
]
const failedJobs = [
new JobImpl("d", true),
new JobImpl("e", false),
new JobImpl("f", true),
]
const throwingJobs = [
new JobImpl("g", true),
new JobImpl("g", true, "undefined problem"),
new JobImpl("i", true),
]
executeUntilFailed(successJobs)
.then((res) => console.log("resolved", res))
.catch((err) => console.log("rejected", err))
executeUntilFailed(failedJobs)
.then((res) => console.log("resolved", res))
.catch((err) => console.log("rejected", err))
executeUntilFailed(throwingJobs)
.then((res) => console.log("resolved", res))
.catch((err) => console.log("rejected", err))
<!-- end snippet -->
Just as an alternative, you could create a generator and then use the for await ... of syntax:
function * chainJobs(jobs) {
for (const job of jobs) {
yield job.execute().catch(err => new Error(`${job.name}: ${err.message}`));
}
}
async function executeUntilFailed(jobs) {
for await (const result of chainJobs(jobs)) {
if (!result) return false;
if (result instanceof Error) throw result;
}
return true;
}
You could achieve this either with a reduce function or a for of loop, i will show an implementation in a for of
async function executeUntilFailed(jobs) {
for (const job of jobs) {
try {
// notice that if a job resolved with false then it is considered a successful job
// This is normal and a promise resolved with false should not be considered an error
await job.execute()
// if u want based on your description to resolve the whole promise with false if one of promises resolved with false you could do
// const jobResult = await job.execute()
// if (jobResult === false) {
// return Prmise.resolve(false)
// }
} catch (err) {
return Promise.reject(new Error(`${job.name}_${err.toString()}`))
}
}
return Promise.resolve(true)
}
lets see the function in action
const successJobs = [{
name: "a",
execute: () => Promise.resolve(1)
},
{
name: "b",
execute: () => Promise.resolve(2),
},
{
name: "c",
execute: () => Promise.resolve(3)
},
]
const failedJobs = [{
name: "a",
execute: () => Promise.resolve(1)
},
{
name: "b",
execute: () => Promise.reject(new Error("undefined problem")),
},
{
name: "c",
execute: () => Promise.resolve(3)
},
]
async function executeUntilFailed(jobs) {
for (const job of jobs) {
try {
await job.execute()
} catch (err) {
return Promise.reject(new Error(`${job.name}_${err.toString()}`))
}
}
return Promise.resolve(true)
}
console.log(
executeUntilFailed(successJobs)
.then((res) => console.log("resolved", res))
.catch((err) => console.log("rejected", err))
)
console.log(
executeUntilFailed(failedJobs)
.then((res) => console.log("resolved", res))
.catch((err) => console.log("rejected", err))
)

Javascript Promise resolving in catch block

I have a function sendMail that returns a promise.
module.exports = function sendMail(mailData) {
const smtpConfig = {
service: 'gmail',
auth: {
user: MAIL_USER,
pass: MAIL_PASS
}
};
const trans = nodemailer.createTransport(smtpConfig);
return new Promise((reject, resolve) => {
trans.sendMail(mailData, (err, result) => {
if (err) {
console.log('error', err)
reject(err);
}
else {
console.log('no error', result)
resolve(result);
}
});
});
}
The promise resolves and the logged output from this is:
no error { accepted: [ 'test#test.com' ],
rejected: [],
response: '250 2.0.0 OK 1488900621 j79sm6419250itb.0 - gsmtp',
envelope: { from: '', to: [ 'test#test.com' ] },
messageId: '<511ca80b-0bec-2d06-8f52-81194bcbf26b#My-MacBook-Pro.local>' }
And the email is sent.
However calling the function from another module, the result gets passed to the .catch block as an error.
module.exports = User = {
sendMail(mailData)
.then((result) => {
console.log('here', result)
})
.catch((err) => {
console.log('error', err)
});
}
Produces the identical output but as an error in the catch block:
error { accepted: [ 'test#test.com' ],
rejected: [],
response: '250 2.0.0 OK 1488900621 j79sm6419250itb.0 - gsmtp',
envelope: { from: '', to: [ 'test#test.com' ] },
messageId: '<511ca80b-0bec-2d06-8f52-81194bcbf26b#MY-MacBook-Pro.local>' }
And the .then block never runs.
Any help as to what I'm missing here is greatly appreciated.
Here:
return new Promise((reject, resolve) => {
you mixed up the order of function parameters. Your function will be passed resolve first and reject second no matter how you name it, so your resolve will act as reject and vice versa.
It should be:
return new Promise((resolve, reject) => {
The names of the arguments to the function that you pass to the Promise constructor doesn't matter, but the order matters. So for example you can use this to produce a resolved promise:
new Promise((x, y) => x(123));
// same as Promise.resolve(123);
or this to produce a rejected promise:
new Promise((x, y) => y(123));
// same as Promise.reject(123);
but if you switch the x and y then it will be different - now x rejects:
new Promise((y, x) => x(123));
// same as Promise.reject(123);
and y resolves:
new Promise((y, x) => y(123));
// same as Promise.resolve(123);
See the docs:
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
Syntax
new Promise( /* executor */ function(resolve, reject) { ... } );
So the mystery is not why it doesn't work but why it works in some other module. Maybe it does something like:
Promise.reject(3).catch(e => e).then(val => console.log('val:', val));
but using your function instead of Promise.reject(3) and using some other logic instead of console.log, of course.

RxJS sequence equivalent to promise.then()?

I used to develop a lot with promise and now I am moving to RxJS. The doc of RxJS doesn't provide a very clear example on how to move from promise chain to observer sequence.
For example, I usually write promise chain with multiple steps, like
// a function that returns a promise
getPromise()
.then(function(result) {
// do something
})
.then(function(result) {
// do something
})
.then(function(result) {
// do something
})
.catch(function(err) {
// handle error
});
How should I rewrite this promise chain in the RxJS style?
For data flow (equivalent to then):
Rx.Observable.fromPromise(...)
.flatMap(function(result) {
// do something
})
.flatMap(function(result) {
// do something
})
.subscribe(function onNext(result) {
// end of chain
}, function onError(error) {
// process the error
});
A promise can be converted into an observable with Rx.Observable.fromPromise.
Some promise operators have a direct translation. For instance RSVP.all, or jQuery.when can be replaced by Rx.Observable.forkJoin.
Keep in mind that you have a bunch of operators that allows to transform data asynchronously, and to perform tasks that you cannot or would be very hard to do with promises. Rxjs reveals all its powers with asynchronous sequences of data (sequence i.e. more than 1 asynchronous value).
For error management, the subject is a little bit more complex.
there are catch and finally operators too
retryWhen can also help to repeat a sequence in case of error
you can also deal with errors in the subscriber itself with the onError function.
For precise semantics, have a deeper look at the documentation and examples you can find on the web, or ask specific questions here.
This would definitely be a good starting point for going deeper in error management with Rxjs : https://xgrommx.github.io/rx-book/content/getting_started_with_rxjs/creating_and_querying_observable_sequences/error_handling.html
A more modern alternative:
import {from as fromPromise} from 'rxjs';
import {catchError, flatMap} from 'rxjs/operators';
fromPromise(...).pipe(
flatMap(result => {
// do something
}),
flatMap(result => {
// do something
}),
flatMap(result => {
// do something
}),
catchError(error => {
// handle error
})
)
Also note that for all this to work, you need to subscribe to this piped Observable somewhere, but I assume it's handled in some other part of the application.
Update May 2019, using RxJs 6
Agree with the provided answers above, wished to add a concrete example with some toy data & simple promises (with setTimeout) using RxJs v6 to add clarity.
Just update the passed id (currently hard-coded as 1) to something that does not exist to execute the error handling logic too. Importantly, also note the use of of with catchError message.
import { from as fromPromise, of } from "rxjs";
import { catchError, flatMap, tap } from "rxjs/operators";
const posts = [
{ title: "I love JavaScript", author: "Wes Bos", id: 1 },
{ title: "CSS!", author: "Chris Coyier", id: 2 },
{ title: "Dev tools tricks", author: "Addy Osmani", id: 3 }
];
const authors = [
{ name: "Wes Bos", twitter: "#wesbos", bio: "Canadian Developer" },
{
name: "Chris Coyier",
twitter: "#chriscoyier",
bio: "CSS Tricks and CodePen"
},
{ name: "Addy Osmani", twitter: "#addyosmani", bio: "Googler" }
];
function getPostById(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const post = posts.find(post => post.id === id);
if (post) {
console.log("ok, post found!");
resolve(post);
} else {
reject(Error("Post not found!"));
}
}, 200);
});
}
function hydrateAuthor(post) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const authorDetails = authors.find(person => person.name === post.author);
if (authorDetails) {
post.author = authorDetails;
console.log("ok, post hydrated with author info");
resolve(post);
} else {
reject(Error("Author not Found!"));
}
}, 200);
});
}
function dehydratePostTitle(post) {
return new Promise((resolve, reject) => {
setTimeout(() => {
delete post.title;
console.log("ok, applied transformation to remove title");
resolve(post);
}, 200);
});
}
// ok, here is how it looks regarding this question..
let source$ = fromPromise(getPostById(1)).pipe(
flatMap(post => {
return hydrateAuthor(post);
}),
flatMap(post => {
return dehydratePostTitle(post);
}),
catchError(error => of(`Caught error: ${error}`))
);
source$.subscribe(console.log);
Output Data:
ok, post found!
ok, post hydrated with author info
ok, applied transformation to remove title
{ author:
{ name: 'Wes Bos',
twitter: '#wesbos',
bio: 'Canadian Developer' },
id: 1 }
The key part, is equivalent to the following using plain promise control flow:
getPostById(1)
.then(post => {
return hydrateAuthor(post);
})
.then(post => {
return dehydratePostTitle(post);
})
.then(author => {
console.log(author);
})
.catch(err => {
console.error(err);
});
If I understood correctly, you mean consuming the values, in which case you use sbuscribe i.e.
const arrObservable = from([1,2,3,4,5,6,7,8]);
arrObservable.subscribe(number => console.log(num) );
Additionally, you can just turn the observable to a promise using toPromise() as shown:
arrObservable.toPromise().then()
if getPromise function is in a middle of a stream pipe you should simple wrap it into one of functions mergeMap, switchMap or concatMap (usually mergeMap):
stream$.pipe(
mergeMap(data => getPromise(data)),
filter(...),
map(...)
).subscribe(...);
if you want to start your stream with getPromise() then wrap it into from function:
import {from} from 'rxjs';
from(getPromise()).pipe(
filter(...)
map(...)
).subscribe(...);
As far as i just found out, if you return a result in a flatMap, it converts it to an Array, even if you returned a string.
But if you return an Observable, that observable can return a string;
This is how I did it.
Previously
public fetchContacts(onCompleteFn: (response: gapi.client.Response<gapi.client.people.ListConnectionsResponse>) => void) {
const request = gapi.client.people.people.connections.list({
resourceName: 'people/me',
pageSize: 100,
personFields: 'phoneNumbers,organizations,emailAddresses,names'
}).then(response => {
onCompleteFn(response as gapi.client.Response<gapi.client.people.ListConnectionsResponse>);
});
}
// caller:
this.gapi.fetchContacts((rsp: gapi.client.Response<gapi.client.people.ListConnectionsResponse>) => {
// handle rsp;
});
After(ly?)
public fetchContacts(): Observable<gapi.client.Response<gapi.client.people.ListConnectionsResponse>> {
return from(
new Promise((resolve, reject) => {
gapi.client.people.people.connections.list({
resourceName: 'people/me',
pageSize: 100,
personFields: 'phoneNumbers,organizations,emailAddresses,names'
}).then(result => {
resolve(result);
});
})
).pipe(map((result: gapi.client.Response<gapi.client.people.ListConnectionsResponse>) => {
return result; //map is not really required if you not changing anything in the response. you can just return the from() and caller would subscribe to it.
}));
}
// caller
this.gapi.fetchContacts().subscribe(((rsp: gapi.client.Response<gapi.client.people.ListConnectionsResponse>) => {
// handle rsp
}), (error) => {
// handle error
});
RxJS sequence equivalent to promise.then()?
For example
function getdata1 (argument) {
return this.http.get(url)
.map((res: Response) => res.json());
}
function getdata2 (argument) {
return this.http.get(url)
.map((res: Response) => res.json());
}
getdata1.subscribe((data1: any) => {
console.log("got data one. get data 2 now");
getdata2.subscribe((data2: any) => {
console.log("got data one and two here");
});
});

Categories