How to make two api calls using Promise.all within Angular9? - javascript

I making an api call using Promise.all as below:
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
});
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
The above code is working fine.
Now I want to make another api call after the successful completion of this.serviceB.retry(oretry).
The second api is this.serviceB.createDbEntry(sentry) and sentry looks as below:
const sretry: SDInterface = {
hostName,
Id: this.Id.slice(0, this.Id.length),
reason: this.reason
};
And, I am doing it as below
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
const sretry: SDInterface = {
hostName,
Id: this.Id.slice(0, this.Id.length),
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
this.serviceB.createDbEntry(sentry).subscribe(resolve);
});
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
The above code is giving an error:
error: "SequelizeValidationError: string violation: Id cannot be an array or an object"
It is looks like it is not calling the second api for every Id

You may want to take a look a forkJoin
import { Observable, forkJoin } from 'rxjs';
And then
ngOnInit() {
let one = this.http.get('some/api/1') //some observable;
let two = this.http.get('some/api/2') // another observable;
forkJoin([one, tow]).subscribe(response => {
// results[0] is our one call
// results[1] is our second call
let var1 = response[1];
let var2 = response[0];
}/*, error => { in case error handler } */);
}

Wouldn't it be better to use Promise.all() once more?
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
});
})
.then(() => {
return Promise.all(this.Id.slice(0, this.Id.length).map(id => {
return new Promise((resolve, reject) => {
const sretry: SDInterface = {
hostName,
Id: id,
reason: this.reason
};
this.serviceB.createDbEntry(sentry).subscribe(resolve);
});
})
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
And using toPromise() will make the code more concise.
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
return this.serviceB.retry(oretry).toPromise();
})
.then(() => {
return Promise.all(this.Id.slice(0, this.Id.length).map(id => {
const sretry: SDInterface = {
hostName,
Id: id,
reason: this.reason
};
this.serviceB.createDbEntry(sentry).toPromise();
})
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});

Use combineLatest, in Angular we use RxJs not promises.
combineLatest(
[this.http.get('call1'), this.http.get('call2')]
).subscribe(([result1, result2]) => {
// do stuff with result1 and result2
});

promise.all takes input in an array and gives response in an array,
Create 2 functions each with your asynchronous logic returning a promise,
Say funcA and funcB, then use below to invoke them parellely
Promise.all([funcA(this.hostName), funcB(this.id)])
.then(respones => {
console.log(responses[0]); //return value for funcA
console.log(responses[1]); //return value for funcB
})
.catch(err => console.log(err));
I am assuming your logic of functions are correct, I just copy-pasted from your question and gave them structure
const funcA = (hostName) => {
hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
});
});
});
}
const funcB = (Id) => {
Id.slice(0, this.Id.length).map(id => {
return new Promise((resolve, reject) => {
const sretry: SDInterface = {
hostName,
Id: id,
reason: this.reason
};
this.serviceB.createDbEntry(sentry).subscribe(resolve);
});
})
}

Related

async function not getting result

I am trying to get the result of an async function in my calculateOrderAmount function but it returns undefined.
The console.log in the called function returns the good result, but inside calculateOrderAmount, I get undefined. Here is my code:
getMultiStrats = async () => {
await MultiStrats.findOne({}, (err, multiStrats) => {
if (err) {
return err
}
if(!multiStrats) {
return console.log('MultiStrat not found')
}
console.log('returns MultiStrat: ' + multiStrats)
return multiStrats
})
.catch(err => console.log(err))
}
async function calculateOrderAmount(balance, ticker){
const multiState = await StrategyController.getMultiStrats().catch((err) => console.log(err))
console.log('multiState: ' + multiState)
some logic
}
Here is the console log:
multiState: undefined
returns MultiStrat: {
_id: 5ff73c74d1135b39fc709b80,
positionsCount: 1,
inTradeCount: 0,
__v: 0
}
What did I miss? Thanks you very much for your time!
The current approach is pretty unclear--there's no need for .catch, async, await all at once. return multiStrats returns from the inside of the callback, not from getMultiStrats. The async/await on getMultiStrats is superfluous, just adding another promise wrapper that doesn't accomplish anything.
Given that findOne as shown here uses a callback rather than a promise, you can either use callbacks all the way or you can promisify findOne as follows, using .then and .catch in the caller:
const MultiStrats = {
findOne: (obj, cb) => cb(null, "I'm a multistrat!")
};
const StrategyController = {
getMultiStrats: () => new Promise((resolve, reject) =>
MultiStrats.findOne({}, (err, multiStrats) => {
if (err) {
return reject(err);
}
else if (multiStrats) {
return resolve(multiStrats);
}
reject(Error("Multistrat not found"));
})
)
};
const calculateOrderAmount = (balance, ticker) =>
StrategyController
.getMultiStrats()
.then(multiState => {
console.log('multiState: ' + multiState)
// some logic
})
.catch(err => console.error(err))
;
calculateOrderAmount();
Or use async/await and try/catch:
const MultiStrats = {
findOne: (obj, cb) => cb(null, "I'm a multistrat!")
};
const StrategyController = {
getMultiStrats: () => new Promise((resolve, reject) =>
MultiStrats.findOne({}, (err, multiStrats) => {
if (err) {
return reject(err);
}
else if (multiStrats) {
return resolve(multiStrats);
}
reject(Error("Multistrat not found"));
})
)
};
const calculateOrderAmount = async (balance, ticker) => {
try {
const multiState = await StrategyController.getMultiStrats();
console.log('multiState: ' + multiState)
// some logic
}
catch (err) {
console.error(err);
}
};
calculateOrderAmount();
If this is MongoDB's findOne and already returns a promise, then you can just return the promise to the caller, optionally awaiting it and throwing for the null result:
const MultiStrats = {
findOne: async query => "I'm a multistrat!"
};
const StrategyController = {
getMultiStrats: async () => {
const result = await MultiStrats.findOne({});
if (result) {
return result;
}
throw Error("Multistrat not found");
}
};
const calculateOrderAmount = (balance, ticker) =>
StrategyController
.getMultiStrats()
.then(multiState => {
console.log('multiState: ' + multiState);
// some logic
})
.catch(err => console.error(err))
;
calculateOrderAmount();
You cannot return values from an inner callback and reach the outer function, I would suggest
1- only use promises
2- wrap your code with promise to be sure that await will return the async result the way you expect to have.
getMultiStrats = async () => {
return new Promise((resolve, reject) => {
MultiStrats.findOne({}, (err, multiStrats) => {
if (err) {
return err
}
if (!multiStrats) {
console.log('MultiStrat not found')
reject('MultiStrat not found')
}
console.log('returns MultiStrat: ' + multiStrats)
resolve(multiStrats);
})
.catch(err => {
console.log(err);
reject(err)
})
})
}
async function calculateOrderAmount(balance, ticker) {
try {
const multiState = await StrategyController.getMultiStrats()
console.log('multiState: ' + multiState)
// some logic
} catch (error) {
console.error(error);
}
}
Assuming that you use mongoose. I suggest using the promise interface like described in the documentation.
const getMultiStrats = async () => {
const query = MultiStrats.findOne({});
let multiStrats;
try {
multiStrats = await query.exec();
} catch (error) {
return error;
}
if (multiStrats) {
console.log("returns MultiStrat: " + multiStrats);
} else {
console.log("MultiStrat not found");
}
return multiStrats;
}
I would personally not return the error, but instead just let the error be thrown. With the above code the caller of getMultiStrats has to figure out if there return value is the expected result or an error. If you don't catch the error, it is thrown further up to the caller.
const getMultiStrats = async () => {
const multiStrats = await MultiStrats.findOne({}).exec();
if (multiStrats) {
console.log("returns MultiStrat: " + multiStrats);
} else {
console.log("MultiStrat not found");
}
return multiStrats;
}
You can further simplify this if you where to leave the console.log of of the equation.
const getMultiStrats = () => MultiStrats.findOne({}).exec();

Need help testing promise chains using jest

I have a function that looks like the following:
const func = () =>
new Promise((resolve, reject) =>
asyncFunc()
.then(data =>
axios.post('someAPI', {
data
})
)
.then(response => {
asyncFunc1(response.data)
resolve(response.data)
})
.catch(err => reject(err)
);
I have read through the documentation of jest and mocking async functions, but it does not seem to cover the scope of this function. Can someone provide some insight into testing functions of this complexity?
EDIT:
Here is the function and current testing:
export const refreshAuth = () =>
new Promise((resolve, reject) =>
getRefreshToken()
.then(refreshJWT =>
axios.post(`${SomeAPI.auth}/refresh`, {
refreshJWT
})
)
.then((res: AxiosResponse<JWTData>) => {
onSignIn(res.data.accessJWT, res.data.refreshJWT);
resolve(res.data.accessJWT);
})
.catch(err => {
console.error('failed to refresh the access token', err);
reject(err);
})
);
export const onSignIn = (access: string, refresh: string) =>
Promise.all([
SecureStore.setItemAsync(REFRESH_KEY, refresh),
SecureStore.setItemAsync(ACCESS_KEY, access)
]);
export const getRefreshToken = () => SecureStore.getItemAsync(REFRESH_KEY);
And Testing:
describe('OAuth2', () => {
it('Runs all promise chains within refreshAuth()', async done => {
const data = {
data: {
accessJWT: 'token',
refreshJWT: 'refresh'
}
};
const getItemAsync = jest.fn().mockResolvedValue({ refreshToken: 'refresh' });
const mockOnSignIn = jest.fn().mockResolvedValue({});
const mockPostAxios = mockedAxios.post.mockResolvedValue(data);
return OAuth2.refreshAuth().then(() => {
expect(getItemAsync).toHaveBeenCalled();
expect(mockPostAxios).toHaveBeenCalled();
expect(mockOnSignIn).toHaveBeenCalledWith(
data.data.accessJWT,
data.data.refreshJWT
);
});
});
});

Trying to refactor a promisified function in to try-catch block

I am trying to refactor this code using try-catch blocks:
export const authorizeConnectyCube = async (accessToken) => {
const userCredentials = {
provider: 'firebase_phone',
'firebase_phone[project_id]': "xxxxxxxx",
'firebase_phone[access_token]': accessToken,
};
await createSession();
return new Promise((resolve, reject) => {
ConnectyCube.login(userCredentials, (error, user) => {
user ? resolve(user) : reject(error);
})
}).catch(error => console.log(error));
}
const createSession = () => {
return new Promise((resolve, reject) => {
ConnectyCube.createSession((error, session) => {
session ? resolve(session.user) : reject(error)
})
}).catch(error => console.log(error));
}
However I'm not getting the same result - the asynchronousity seems to be being handled differently. Here is my attempt at refactoring:
export const authorizeConnectyCube = async (accessToken) => {
const userCredentials = {
provider: 'firebase_phone',
'firebase_phone[project_id]': "xxxxxxxxxx",
'firebase_phone[access_token]': accessToken,
};
await createSession();
try {
ConnectyCube.login(userCredentials, (error, user) => {
return user;
})
}
catch (error) {
console.log(error)
}
}
const createSession = () => {
try {
ConnectyCube.createSession((error, session) => {
return session.user
})
} catch (error) {
console.log(error);
}
}
Is there any particular part of what I'm wrong? Thanks.
Callback-based APIs don't readily turn into something you can use for async/await (which under the hood uses promises). You'll have to "promisify" them first (i.e. wrap them in promises).
Here's an example of what I'm trying to say:
// Promisify these callback-based APIs.
const login = userCredentials => {
return new Promise((resolve, reject) => {
ConnectyCube.login(userCredentials, (error, user) => {
user ? resolve(user) : reject(error);
})
})
})
const createSession = () => {
return new Promise((resolve, reject) => {
ConnectyCube.createSession((error, session) => {
session ? resolve(session.user) : reject(error)
})
})
})
// Then use them in an async function
export const authorizeConnectyCube = async (accessToken) => {
const userCredentials = {
provider: 'firebase_phone',
'firebase_phone[project_id]': "xxxxxxxx",
'firebase_phone[access_token]': accessToken,
}
try {
await createSession()
return login(userCredentials)
} catch (e) {
console.warn(e)
}
}
Also, async functions return promises, with the resolved value being the return value, and the rejected value being any uncaught error thrown inside. A value wrapped in a promise as return value for an async function is redundant.
If you're using Node 8+, it has a utility called promisify which accepts a callback-based API and returns a promise-returning version of it.

returning the result from my function

How do I return my result please,
I get an error
Can't find variable: convertToArray
I have a function in my api.js
export function apiGet() {
return axios
.get('http')
.then(res => {
const convertToArray = []
for (const key in res.data) {
convertToArray.push({ ...res.data[key], id: key })
//console.log confirms my convertToArray has the info I expect in it
}
return convertToArray;
})
.catch(e => {
console.log(e);
})
}
in my vuex store I have
// Get list of cases
loadCasess ({ commit, context }) {
return new Promise ((resolve, reject) => {
apiGet()
resolve(commit('LIST_CASES', convertToArray))
})
.catch(e => {
console.error(e)
// reject('/')
})
},
You're receiving this error because convertToArray doesn't exist in the context of your Vuex Store. You're returning it, but doesn't mean that on the function that calls the apiGet() it will exist. You should write like:
// Get list of cases
loadCases ({ commit, context }) {
return new Promise ((resolve, reject) => {
const convertToArray = apiGet()
resolve(commit('LIST_CASES', convertToArray))
}).catch(e => {
console.error(e)
// reject('/')
})
}, // ...

How to nested promise.all

I'm using es6 and have the following promises. What i want is the next Promise.all to wait for previous Promise.all to be completed before execute the next one. I have tried with the below codes but it's not working, only Promise 1 is resolved.
var deletePromises = [];
arr.menuItems.forEach((item, idx) => {
if (item.replace) {
deletePromises.push(deleteFromFirebase(user.uid, item));
}
});
// Promise 1
Promise.all(deletePromises).then(res1 => {
var uploadPromises = [], updateRecordPromises = [];
arr.menuItems.forEach((item, idx) => {
uploadPromises.push(uploadToFirebase(user.uid, item));
});
// Promise 2
Promise.all(uploadPromises).then(res2 => {
arr.menuItems.forEach((item, idx) => {
item.replace = false;
updateRecordPromises.push(updateRecord(user.uid, item));
});
// Promise 3
Promise.all(updateRecordPromises).then(res3 => {
console.log('All promise execute with successfully');
});
});
});
MarkM Answer
Try to use chaining as Mark suggest but the problem still there. I had found where the problem was, it is uploadPromises that never get resolved and then is never get called.
uploadToFirebase function
Stuck here, but the file is successfully uploaded. I can see all the files.
const uploadToFirebase = (userid, item) => {
return new Promise((resolve, reject) => {
const uploadUri = Platform.OS === "ios"
? RNFetchBlob.wrap(item.pic_url.replace("file://", ""))
: RNFetchBlob.wrap(item.pic_url);
Blob.build(uploadUri, {
type: "image/png;"
}).then(blob => {
// upload image using Firebase SDK
firebase
.storage()
.ref("menu_items")
.child(userid)
.child(item.new_filename)
.put(blob, { contentType: "image/png" })
.then(snapshot => {
console.log("Promise resolve: ", snapshot);
resolve(snapshot);
blob.close();
})
.catch(error => {
reject(error.message);
});
});
});
};
Updated code
console.log('Print res2') is not printed
var deletePromises = [],
uploadPromises = [],
updateRecordPromises = [];
arr.menuItems.forEach((item, idx) => {
if (item.replace) {
deletePromises.push(deleteFromFirebase(user.uid, item));
}
});
Promise.all(deletePromises)
.then(res1 => {
console.log("Print res1:", res1);
arr.menuItems.forEach((item, idx) => {
uploadPromises.push(uploadToFirebase(user.uid, item));
});
return Promise.all(uploadPromises);
})
.then(res2 => {
console.log("Print res2:", res2);
dispatch({ type: MENU_UPDATE_SUCCESS, payload: arr });
dispatch(reset("menuItem"));
})
.catch(error => {
console.log("Print error:", error);
});
You don't need to nest the Promises, you can return your new Promise.all from then(), which will let you chain them. Easier with an example:
var arr = [1, 2, 3, 4, 5, 6]
function slowF(i) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(i), i*200)
})
}
var p = arr.map((i) => slowF(i))
Promise.all(p)
.then((p) => {
console.log("In first Promise.all resolving: ", p)
var newP = p.map(i => slowF(i) )
return Promise.all(newP)
})
.then((p)=>{
console.log("In second Promise.all resolving: ", p)
})
.catch((e) => console.log("error: ", e))
I found the solution. It is because one of menuItems array prop -> new_filename is empty, thats why promise never get resolved. So, i just need to add empty checking for each of item prop and then the promise was resolved properly. Thanks to #MarkM for the answer. Now the code much more cleaner and easier to read compare to the nested promise.
Promise.all(deletePromises)
.then(res1 => {
console.log("Print res1:", res1);
arr.menuItems.forEach((item, idx) => {
if (!isEmpty(item.new_filename)) {
uploadPromises.push(uploadToFirebase(user.uid, item));
}
});
return Promise.all(uploadPromises);
})
.then(res2 => {
console.log("Print res2:", res2);
dispatch({ type: MENU_UPDATE_SUCCESS, payload: arr });
dispatch(reset("menuItem"));
})
.catch(error => {
console.log("Print error:", error);
});

Categories