I am using a library that calls an API, and I am waiting for Promise return to receive an Array.
However, even though I expect 2 elements in ActivityItem array, sometimes I receive only first of them (the one that appears first (Item1). From my point of view, I implemented the Promise incorrectly and there should be a mistake in the way I return them, but I miss seeing it.
Here I call the function that uses that should return Promise:
componentDidMount() {
this.getDataFromKit(ONEDAYINTERVAL).then(result => {
this.sendDataToServer(result); //sending to backend
}).catch(e => console.error);
}
And here is a method itself:
getDataFromKit(dateFrom) {
return new Promise((resolve) => {
AppleKit.initKit(KitPermissions.uploadBasicKitData(), (err, results) => {
if (err) {
return;
}
AppleKit.getSamples(dateFrom, (err, results) => {
if (err) {
return resolve([]);
}
const newData = results.map(item => {
return { ...item, name: "Item1" };
});
const allData = [...this.state.ActivityItem, ...newData];
this.setState({ ActivityItem: allData });
resolve(allData);
});
// if I delete the code below it will work just fine always grabbing only one item.
new Promise((resolve) => {
AppleKit.getSamplesSecondMethod(dateFrom, (err, results) => {
if (err) {
return resolve([]);
}
const newData = results.map(item => {
return { ...item, name: "Item2" };
});
const allData = [...this.state.ActivityItem, ...newData];
this.setState({ ActivityItem: allData });
resolve(allData);
});
});
});
})
}
The main issue here is I guess: how can I return multiple promises from this one method?
The problem as I see it, is your second block of code doesn't get run because you are resolving the promise in the first block. The way you have it coded, you will want to resolve that promise only once all your async operations have completed. I modified your code, but have not tested it. It may need to add the .then method to make sure the async data is returned before resolving the initial promise.
What happens if you try this ?
UPDATE
It looks like the below code solved your problem as you accepted my answer. However, I did re-write it before I realized you accepted so I will add the new updated code in case that will help you or someone else.
Original Answer
getDataFromKit(dateFrom) {
const thenable = new Promise((resolve) => {
AppleKit.initKit(KitPermissions.uploadBasicKitData(), (err, results) => {
if (err) {
return;
}
AppleKit.getSamples(dateFrom, (err, results) => {
if (err) {
return resolve([]);
}
const newData = results.map(item => {
return {
...item,
name: "Item1"
};
});
resolve(newData);
});
});
})
.then((newData) => {
AppleKit.initKit(KitPermissions.uploadBasicKitData(), (err, results) => {
if (err) {
return;
}
AppleKit.getSamplesSecondMethod(dateFrom, (err, results) => {
if (err) {
return;
}
var stateData = this.state.ActivityItem;
const addData = results.map(item => {
return {
...item,
name: "Item2"
};
});
stateData = [...stateData, ...newData];
stateData = [...stateData, ...addData];
this.setState({
ActivityItem: stateData
});
});
});
});
return thenable;
}
Updated Code using Promise.all
getDataFromKit(dateFrom) {
return new Promise((resolve) => {
const promise1 = new Promise((resolve) => {
AppleKit.initKit(KitPermissions.uploadBasicKitData(), (err, results) => {
if (err) {
return Promise.reject(err);
}
AppleKit.getSamples(dateFrom, (err, results) => {
if (err) {
return Promise.reject(err);
}
const newData = results.map(item => {
return {
...item,
name: "Item1"
};
});
return Promise.resolve(newData);
});
});
});
const promise2 = new Promise((resolve) => {
AppleKit.initKit(KitPermissions.uploadBasicKitData(), (err, results) => {
if (err) {
return Promise.reject(err);
}
AppleKit.getSamplesSecondMethod(dateFrom, (err, results) => {
if (err) {
return Promise.reject(err);
}
const moreData = results.map(item => {
return {
...item,
name: "Item2"
};
});
return Promise.resolve(moreData);
});
});
});
Promise.all([promise1, promise2])
.then(([result1, result2]) => {
var nArrays = [result1, result2, this.state.ActivityItem];
const finalResult = [].concat(...nArrays);
return Promise.resolve(finalResult);
});
});
}
Related
This question already has answers here:
How do I convert an existing callback API to promises?
(24 answers)
Closed 9 months ago.
async function bringData() {
return new Promise(resolve => {
setTimeout(() => {
resolve(con.query('SELECT company, industry FROM my_table', function (err, result) {
if (err) {
console.error(err);
throw 'Error getting data from google_businesses table';
} else {
return result;
}
}));
}, 1000);
});
}
(async () => {
// return data from brindData function
let getData = await bringData();
console.log(getData);
})();
I am calling this function in main but I am always getting error. I am kind of new to node so if please anybody can guide me or is there any other way to do this such that when I have got my result I can return it.
Call resolve with the result from your query.The con.query looks like another asynchronous thing:
con.query('...', (err, result) => {
if (!err)
resolve(result);
});
You can try this.
async function bringData() {
return new Promise((resolve, reject) => {
con.query('SELECT company, industry FROM my_table', (err, result) => {
if (err) {
reject(err);
}
resolve(result);
})
});
}
(async () => {
// execute bringData function
try {
let getData = await bringData();
console.log(getData);
} catch (e) {
console.error(e);
}
})();
Edited with timeout
async function bringData() {
return new Promise((resolve, reject) => {
con.query('SELECT company, industry FROM my_table', (err, result) => {
if (err) {
reject(err);
}
resolve(result);
})
});
}
function timeout(seconds = 10) {
return new Promise(resolve => {
setTimeout(resolve, 10 * 1000)
})
}
(async () => {
// execute bringData function
try {
await timeout();
let getData = await bringData();
console.log(getData);
} catch (e) {
console.error(e);
}
})();
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();
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);
});
})
}
I'm trying to return the result from this function :
var YouTube = require('youtube-node');
var youTube = new YouTube();
youTube.setKey('XXXXXXXXXXXX');
var result = youTube.search('World War z Trailer', 2,
function(error, result) {
if (error) {
console.log(error);
} else {
return result
}})
console.log(result)
But then I only get undefined as a result in console.
You need to wait for the response.
This simple answer is to place the console.log(result) inside the callback.
Or you can wrap the search method in a promise:
var promise = new Promise((resolve, reject) => {
youTube.search('World War z Trailer', 2, function (error, result) {
if (error) {
reject(error);
}
else {
resolve(result);
}
})
});
Then you can wait for the response using .then() like this:
promise.then((result) => {
console.log(result);
}).catch((err) => {
console.error(err);
});
Or using await/async:
(async () => {
try {
const result = await promise;
console.log(result);
} catch (err) {
console.error(err)
}
})();
Last function here is a callback function, it doesn't return value in way you want. Read how it works somewhere, for example here.
Your issue could be resolved with Promise in this way
var search = new Promise(
(resolve, reject) => {
youTube.search('World War z Trailer', 2, function(error, result) {
if (error) { reject(error); }
resolve(result);
})
}
);
(async () => {
var result = await search;
console.log(result);
})()
const xml2json = require("xml-to-json");
const convertXMITOJSON = () => {
xml2json(
{
input: "./sequence_diagram.xmi",
output: "./test.json",
},
function (err, result) {
if (err) console.error(err);
else {
return result;
}
}
);
};
let result = convertXMITOJSON();
console.log(result); // undefined
I want to use the value of result outside of this function. But when I return the value of result, it's getting undefined. Why is in this code the value of result outside the function undefined?
Promises are the easiest way to deal with it
const xml2json = require("xml-to-json");
const convertXMITOJSON = () => {
return new Promoise((resolve, reject) => {
xml2json({
input: "./sequence_diagram.xmi",
output: "./test.json",
},
function(err, result) {
if (err) {
console.error(err);
reject(err)
} else {
resolve(result);
}
}
);
})
};
convertXMITOJSON().then(result => {
console.log(result);
}
The code could be changed with async and await