What is wrong with this async-await code (javascript)? - javascript

I am very new to asynchronous JavaScript. I am trying to add a new member to a zoo and then display the updated list of zoo animals in the following code. the new animal is being admitted successfully but the method to display updated list of zoo animals is not working. Can anyone plz point out what is going on here?
let zoo = [
{ animal: "elephant", age: 15 },
{ animal: "rhino", age: 10 },
{ animal: "tiger", age: 6 },
];
const admit = (animal) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
zoo.push(animal);
resolve: console.log(
`new ${animal.animal} added. now ${zoo.length} animals.`
);
//reject: console.log("something went wrong");
}, 2000);
});
};
const displayZoo = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve: console.table(zoo);
reject: console.log("something went wrong while displaying animals");
}, 3000);
});
};
const doSomething = async () => {
try {
let admission = await admit({ animal: "lion", age: 13 });
await displayZoo();
console.log("everything went fine");
} catch (err) {
console.log(err);
}
};
doSomething();

resolve and reject in the Promise constructor are functions. With async/await syntax, you call resolve with the value to return to the function that is await-ing or call reject with an object to throw.
Your code is in charge of determining when an error has occurred to decide whether to call resolve or reject.
let zoo = [
{ animal: "elephant", age: 15 },
{ animal: "rhino", age: 10 },
{ animal: "tiger", age: 6 },
];
const admit = (animal) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
zoo.push(animal);
resolve(`new ${animal.animal} added. now ${zoo.length} animals.`);
// `await admit()` will return this string ^
}, 2000);
});
};
const displayZoo = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) { // To simulate some failure condition
console.table(zoo);
resolve();
// `await displayZoo()` will return undefined (void function)
} else {
reject('There was an error');
// `await displayZoo()` will throw this string ^
}
}, 3000);
});
};
const doSomething = async () => {
try {
let admission = await admit({ animal: "lion", age: 13 });
console.log(admission);
await displayZoo();
console.log("everything went fine");
} catch (err) {
console.log(err);
}
};
doSomething();

Related

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);

Combining async operations into one synchronous operation

I have a requirement where I need to call multiple APIs asynchronously and combine the result of all the APIs and return it. The issue here is, I need to use this complete method as synchronous wherever I will be using it.
Note: I can't use any of the libraries/frameworks.
function op1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
firstName: "Nikhil"
});
}, 1000);
});
}
function op2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
lastName: "Goyal"
});
}, 500);
});
}
function getName() {
// Some implementation here
}
const name = getName();
console.log(name.firstName);
console.log(name.lastName);
Try using async and await.
function op1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
firstName: "Nikhil"
});
}, 1000);
});
}
function op2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
lastName: "Goyal"
});
}, 500);
});
}
async function getName() {
const a = await op1();
const b = await op2();
return {firstName: a.firstName, lastName: b.lastName}
}
getName().then(name=> {
console.log(name.firstName);
console.log(name.lastName);
}
);

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))
)

Break a chain in promises and async await

I have the following promise :
var aggregatePromise = () => {
return new Promise((resolve, reject) => {
EightWeekGamePlan.aggregate([
{
$match: {
LeadId: { $in: leads },
Week: week
}
},
{
$group: {
_id: {
LeadId: "$LeadId"
},
total: { $sum: "$TotalClaimsToBeClaimedByClientType" }
}
},
{
$match: {
total: { $lte: 5 - howManyClaims }
}
}
])
.then(leads => {
if (leads !== null) {
resolve(leads);
} else {
reject("Couldn't find any Leads");
}
})
.catch(err => {
reject(err);
});
});
};
I call it here :
// Step 2
var callAggregatePromise = async () => {
var result = await aggregatePromise();
return result;
};
And use it here :
//Step 3: make the call
callAggregatePromise().then(result => {
const winners = result.map(m => ({
LeadId: m._id.LeadId
}));
const flattened = winners
.reduce((c, v) => c.concat(v), [])
.map(o => o.LeadId);
console.log(flattened);
// Step 4 - declare 2ND Promise
var updateLeadsPromise = () => {
return new Promise((resolve, reject) => {
EightWeekGamePlan.updateMany(
{
LeadId: {
$in: flattened
},
TargetedToBeClaimedByClientType: groupTarget,
Week: week
},
{
$inc: {
TotalClaimsToBeClaimedByClientType: howManyClaims
}
}
)
.then(leadsUpdated => {
if (leadsUpdated !== null) {
resolve(leadsUpdated);
} else {
reject("Failed to update requested leads!");
}
})
.catch(err => {
reject(err);
});
});
};
//Step 5 : Call 2ND promise
var callUpdateLeadsPromise = async () => {
var resAgg = await updateLeadsPromise();
return resAgg;
};
//Step 6 : make the call to the "updateLeadsPromise"
callUpdateLeadsPromise().then(result1 => {
console.log(result1);
if (result1.ok === 1) {
// TODO
}
});
});
The problem is that Step 4)5)6) are dependent on the result of step 3).
How can I break the chain and make them independent ?
To break a chain you will have to await the Promises in order. It will still be a chain but in a different format. Btw throw away step 2 and 5.
To use await you have to have it inside a async function.
async function doLeadStuff() {
// awaiting main Promise
const result = await aggregatePromise();
const winners = result.map(m => ({
LeadId: m._id.LeadId
}));
const flattened = winners
.reduce((c, v) => c.concat(v), [])
.map(o => o.LeadId);
console.log(flattened);
// awaiting updates
const leadsUpdated = await EightWeekGamePlan.updateMany(
{
LeadId: {
$in: flattened
},
TargetedToBeClaimedByClientType: groupTarget,
Week: week
},
{
$inc: {
TotalClaimsToBeClaimedByClientType: howManyClaims
}
}
)
if (leadsUpdated === null) {
throw new Error("Failed to update requested leads!");
}
console.log(result1);
if (result1.ok === 1) {
// TODO
}
}
// calling async function
doLeadStuff()

Passing an object an array of it's own promises functions to be executed in series?

I would like to achieve the following: I have a class generating an object with a series of methods.
Each of these is likely to be a promise.
From the outside of it should be possible to pick these methods and compose them into an array and ask the same object to execute them in series.
The code should be something like this (of course this does not work, this is why I'm asking):
class Car{
constructor(){}
fuctionA(input){
// do stuffs
}
fuctionB(input){
// do stuffs
}
fuctionC(input){
// do stuffs
}
fuctionD(input){
// do stuffs
}
executeArrayOfFuctions(array){
// execute each function in the array when the previous is resolved
}
}
const car1 = new Car();
const car2 = new Car();
const arrayOfFunctions = [functionA(input), functionC(input), functionA(input)];
car1.executeArrayOfFuctions(arrayOfFunctions);
car2.executeArrayOfFuctions(arrayOfFunctions);
EDIT: The reason because I pass an arbitrary order of functions from outside the object is because I want to be able to have different combinations of those functions every time.
Like for example a choreography, where you can have many dancers able to perform a number of movements, but not always the same, and not the same for everyone.
Sorry, this was not specified well enough.
EDIT: for #Akshay Bande, I do like how you pass a string reference to the internal methods, but still if I try to really go ahead and defines my methods as promises, with a 1-second timeout to be resolved, they are executed all at once.
I did not really get the Promise.resolve() part.
class Car {
constructor() {}
functionA(input) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("A", input);
resolve();
}, 1000);
});
}
functionB(input) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("B", input);
resolve();
}, 1000);
});
}
functionC(input) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("C", input);
resolve();
}, 1000);
});
}
functionD(input) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("D", input);
resolve();
}, 1000);
});
}
executeArrayOfFuctions(array) {
let p = Promise.resolve();
array.forEach(val => {
p = p.then(() => {
this[val.functionName](val.arg);
return Promise.resolve();
});
});
}
}
const car1 = new Car();
const car2 = new Car();
const arrayOfFunctions = [
{ functionName: "functionA", arg: "1" },
{ functionName: "functionC", arg: "2" },
{ functionName: "functionA", arg: "3" }
];
car1.executeArrayOfFuctions(arrayOfFunctions);
//car2.executeArrayOfFuctions(arrayOfFunctions);
3EDIT:
Thanks #AkshayBande for your help, I think this way can work:
class Car {
constructor() {}
functionA(input) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("A", input);
resolve();
}, 1000);
});
}
functionB(input) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("B", input);
resolve();
}, 1000);
});
}
functionC(input) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("C", input);
resolve();
}, 1000);
});
}
functionD(input) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("D", input);
resolve();
}, 1000);
});
}
async executeArrayOfFuctions(array) {
for (let index = 0; index < array.length; index++) {
await this[array[index].functionName](array[index].arg);
}
}
}
const car1 = new Car();
const car2 = new Car();
const arrayOfFunctions = [
{ functionName: "functionA", arg: "1" },
{ functionName: "functionC", arg: "2" },
{ functionName: "functionA", arg: "3" }
];
car1.executeArrayOfFuctions(arrayOfFunctions);
//car2.executeArrayOfFuctions(arrayOfFunctions);
Or like this:
class Car {
constructor() {}
functionsStack = [];
addFunctionA(input) {
this.functionsStack.push({ fn: "functionA", args: input });
return this;
}
addFunctionB(input) {
this.functionsStack.push({ fn: "functionB", args: input });
return this;
}
addFunctionC(input) {
this.functionsStack.push({ fn: "functionC", args: input });
return this;
}
addFunctionD(input) {
this.functionsStack.push({ fn: "functionD", args: input });
return this;
}
functionA(input) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("A", input);
resolve();
}, 1000);
});
}
functionB(input) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("B", input);
resolve();
}, 1000);
});
}
functionC(input) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("C", input);
resolve();
}, 1000);
});
}
functionD(input) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("D", input);
resolve();
}, 1000);
});
}
start() {
return new Promise((resolve, reject) => {
this.executeArrayOfFuctions(this.functionsStack).then(() => {
this.functionsStack = [];
resolve();
});
});
}
async executeArrayOfFuctions(array) {
for (let index = 0; index < array.length; index++) {
await this[array[index].fn](array[index].args);
}
}
}
const car1 = new Car();
const car2 = new Car();
car1
.addFunctionA("1")
.addFunctionB("2")
.addFunctionC("3")
.addFunctionA("5")
.addFunctionD("6")
.addFunctionB("7")
.start()
.then(() => {
console.log(car1.functionsStack);
});
// const arrayOfFunctions = [
// { functionName: "functionA", arg: "1" },
// { functionName: "functionC", arg: "2" },
// { functionName: "functionA", arg: "3" }
// ];
// car1.executeArrayOfFuctions(arrayOfFunctions);
//car2.executeArrayOfFuctions(arrayOfFunctions);
No, don't make it that complicated. With async/await, you don't need to pass functions around. Also the Car object should not be responsible for being able to run async functions sequentially - that's the job of a simple helper function if you really needed it. But just write
class Car {
constructor() {}
async functionA(input) {
// do stuffs
}
async functionB(input) {
// do stuffs
}
async functionC(input) {
// do stuffs
}
async functionD(input) {
// do stuffs
}
}
async function go(car) {
await car.functionA(input);
await car.functionC(input);
await car.functionA(input);
}
go(new Car()); // car 1
go(new Car()); // car 2
Promises are what you need.
class Car{
constructor(){}
functionA(input){
// do stuffs
console.log(input);
}
functionB(input){
// do stuffs
console.log(input);
}
functionC(input){
// do stuffs
console.log(input);
}
functionD(input){
// do stuffs
console.log(input);
}
executeArrayOfFuctions(array){
// execute each function in the array when the previous is resolved
let p = Promise.resolve();
array.forEach((val)=>{
p = p.then(()=>{
this[val.functionName](val.arg);
return Promise.resolve();
})
})
}
}
const car1 = new Car();
const car2 = new Car();
const arrayOfFunctions = [ {functionName: 'functionA', arg:'1'},{functionName: 'functionC', arg:'2'}, {functionName: 'functionA', arg:'3'} ];
car1.executeArrayOfFuctions(arrayOfFunctions);
//car2.executeArrayOfFuctions(arrayOfFunctions);
According to edit added to the question, If you want to execute functions after one-second delay then you can use this code.
class Car {
constructor() {}
delay(time){
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve();
},time)
})
}
functionA(input) {
this.delay(1000).then(()=>{
console.log('A');
})
}
functionB(input) {
this.delay(1000).then(()=>{
console.log('B');
})
}
functionC(input) {
this.delay(1000).then(()=>{
console.log('C');
})
}
functionD(input) {
this.delay(1000).then(()=>{
console.log('D');
})
}
executeArrayOfFuctions(array) {
let p = Promise.resolve();
array.forEach(val => {
p = p.then(() => {
this[val.functionName](val.arg);
return Promise.resolve();
});
});
}
}
const car1 = new Car();
const car2 = new Car();
const arrayOfFunctions = [
{ functionName: "functionA", arg: "1" },
{ functionName: "functionC", arg: "2" },
{ functionName: "functionA", arg: "3" }
];
car1.executeArrayOfFuctions(arrayOfFunctions);
//car2.executeArrayOfFuctions(arrayOfFunctions);

Categories