I have requirements to search given "key" in API returned response. I have to invoke same API multiple times with different inputs.
Upon getting the response from API , i need to search KEY in API response. If the KEY is not present then invoke the same API again with different input and search ...continue .
Basically , until i complete one iteration of searching the KEY in API response, the execution should wait . How can achieve this ? Please suggest.
I have tried with below approach and execution not waiting until it search keys in API response.
for (i=0;i< departments.length;i++)
{
getInformation(departments[i]).
then((response) => {
//verify whether given key present in response
})
.catch((err)) => {
//log error
});
}// end of for loop
}
Note: I would like to continue the Search operation with next keys even if their is any exception in one of the API call.
Thanks
You've to use recursivity
let departments = [xxx]
let myResult = []
const search = (pool, resolve, reject) => {
if (pool.length === 0) {
return resolve()
}
let [department, ...rest] = pool
getInformation(department)
.then(data => {
myResult.push('something')
})
.catch(e => {})
.finally(() => {
search(rest, resolve, reject)
})
}
const searchInDeps = new Promise((resolve, reject) => {
search(departments, resolve, reject)
})
searchInDeps
.then(() => {
// do something with myResult
})
.catch(e => {})
One other way as you need to loop, you can run each promises, and wait for the result of all.
let searches = []
for (i=0;i< departments.length;i++) {
searches.push(new Promise((resolve, reject) => {
getInformation(departments[i])
.then(data => {
resolve('something')
})
.catch(e => resolve('something'))
})
}
Promise.all(searches)
.then(results => {
// array of results
})
.catch(error => {})
Related
I am sending data to a Bluetooth device, and the responses are handled by a listener that's set up during the connection process:
device.connect().then(device => {
device.registerResponseListener((data) => {
// handle response
}
}
I have a separate function that sends data to the device:
const sendData = (device, data) => {
device.write(data);
}
My question is, how can I Promisify this code? I'd like to be able to do
const sendData = (device, data) => {
return new Promise((resolve, reject) => {
device.write(data);
// resolve...?
});
}
But how do I get the resolve into the Bluetooth response listener?
I don't know what API you're using but you can try BluetoothRemoteGATTCharacteristic API. It has writeValueWithResponse method which return Promise.
https://developer.mozilla.org/en-US/docs/Web/API/BluetoothRemoteGATTCharacteristic
If I understood you correctly then you can do it like this
const sendData = async (device, data) => {
const response = await device.write(data);
await Promise.all(device.responseListeners.map(listener => listener(response)))
}
The best possible solution in this case, while still not ideal, was to store the resolve function in variable at a higher scope:
var sendDataResolve;
device.connect().then(device => {
device.registerResponseListener((data) => {
sendDataResolve(data);
}
}
const sendData = (device, data) => {
return new Promise((resolve, reject) => {
sendDataResolve = resolve;
device.write(data);
});
}
...
sendData(device, "data")
.then(result => {
console.log("Got result",result);
});
The caveat is that Promise resolutions are NOT guaranteed to be tied correctly to the original request. This ONLY works with one request at a time.
I have got two data tables I want to query some data from, so the way i thought about doing it is fetch url and based on a field from list, I do fetch(url) once more to get related data from another data table and I want to append them together to display on the browser. The issue here is that for loop iteration doesn't wait until the previous fetch is finished. I am using django_restframework, and it is me trying to fetch data by passing criteria(variables) through urls. can anyone help?
var url = 'http://127.0.0.1:8000/creditor-detail/'+pmt_id+'/supplier/'+crm_spplier_id+'/'
var results = fetch(url)
.then((resp) => resp.json())
.then(function(item){
var list = item
for (var i in list){
getCustomer(list[i].customer_id)
.then(function test(user) {
return user[0].customer_name
});
var spmt = `
<tr id="data-row-${i}">
<td>${list[i].po_no}</td>
<td>${list[i].amount}</td>
<td>${I want return value[user[0].customer_name]}</td>
</tr>
`
wrapper.innerHTML+= spmt
}
})
function getCustomer(customer_id){
var url = 'http://127.0.0.1:8000/user-detail/'+customer_id+'/'
var results = fetch(url)
.then((resp) => resp.json())
.then(function(item){
return item
})
return results
}
I have changed to:
function test() {
const url = 'http://127.0.0.1:8000/creditor-detail/'+pmt_id+'/supplier/'+crm_supplier_id+'/'
let promises = [];
const results = fetch(url)
.then(resp => resp.json())
.then(function (item) {
var list = item;
for (var i in list) {
promises.push(getCusotmer(list[i].customer_id));
console.log(list[i].customer_id)
}
})
Promise.all(promises)
.then((results) => {
console.log("All done", results);
})
.catch((e) => {
console.log(err)
});
}
function getCusotmer(customer_id) {
return new Promise((resolve, reject) => {
const url = 'http://127.0.0.1:8000/customer-detail/' + customer_id+ '/';
fetch(url)
.then(resp => resp.json())
.then((item) => resolve(item))
.catch((err) => {
console.log(err)
reject(err)
})
})
}
test();
And the console looks like this:
All done []length: 0__proto__: Array(0)
1466
1663
I thought based on the promise logic, all done should have been read in the end, am I missing anything here?
It's hard to understand the first time, but here I go.
The simple answer is "You can't do that", javascript works with an 'event loop', it's like a thread containing all the tasks javascript is going to do, when you use an asynchronous task like 'search' it escapes the 'event loop' for being asynchronous . But the 'for loop' is not asynchronous so it will be processed in the 'Event Loop'.
This means that 'fetch' will exit the 'Event Loop' and when its request is completed it will return to the 'Event Loop', in this moment, the 'for loop' was terminate.
But don't worry, you can solve it, how? well, you can iterate the loop and save a new array of promise, await for all of them will complete and in this moment create your 'tr' row
Its an example with your code:
The first function could be like this:
const promises = [];
fetch(url)
.then(resp => resp.json())
.then((item) => {
const list = item;
for (let i in list) {
promises.push(getCustomer(list[i].customer_id))
}
Promise.all(promises).then((resolveAllData) => {
//Do all that you need with your data
//Maybe you can use a for loop for iterate 'responseAllData
});
}).catch((err) => {
//manage your err
})
And the second function could be like this:
function getCustomer(customer_id) {
return new Promise((resolve, reject) => {
const url = 'http://127.0.0.1:8000/user-detail/' + customer_id + '/';
fetch(url)
.then(resp => resp.json())
.then((item) => resolve(item))
.catch((err) => {
//manage your err
reject(err)
})
})
}
A recommendation, learn how JavaScript works and its asynchronism, try not to use 'var', to get better performance and have no problems with scope
I have got a function that invokes when a button is clicked, and I am trying to fetch data through API upon clicking. and below is the function that gets triggered onclick. I have to fetch data through API once and then based on a field from the fetched data, I need to fetch another data from another data table. so, I have designed my coding as below, and my console looks like this:
And the console looks like this:
All done []length: 0__proto__: Array(0)
1466 (customer_id logged during for loop)
1663 (customer_id logged during for loop)
I thought based on the promise logic, all done should have been read in the end, am I missing anything here?
so Ideally, Alldone console should have invoked at the end containing data fetched based on customer_ids 1466, 1663.
I am not sure what I am missing, I am new to javascript as well as stack overflow, so detailed answer would be so much appreciated.
function test(pmt_id, crm_supplier_id) {
const url = 'http://127.0.0.1:8000/creditor-detail/'+pmt_id+'/supplier/'+crm_supplier_id+'/'
let promises = [];
const results = fetch(url)
.then(resp => resp.json())
.then(function (item) {
var list = item;
for (var i in list) {
promises.push(getCustomer(list[i].customer_id));
console.log(list[i].customer_id)
}
})
Promise.all(promises)
.then((results) => {
console.log("All done", results);
})
.catch((e) => {
console.log(err)
});
}
//second function
function getCustomer(customer_id) {
return new Promise((resolve, reject) => {
const url = 'http://127.0.0.1:8000/customer-detail/' + customer_id+ '/';
fetch(url)
.then(resp => resp.json())
.then((item) => resolve(item))
.catch
You run the Promise.all part before you even push anything into the promises array, since the latter part runs asynchronously. You'd need to move the Promises.all part into a .then:
const results = fetch(url)
.then(resp => resp.json())
.then(function (item) {
var list = item;
for (var i in list) {
promises.push(getCustomer(list[i].customer_id));
console.log(list[i].customer_id)
}
})
.then(() => Promise.all(promises))
.then((results) => {
console.log("All done", results);
})
.catch((e) => {
console.log(err)
});
Also, just writing .catch does nothing, you need to call it and pass a handler:
fetch(url)
.then(resp => resp.json())
.then((item) => resolve(item))
.catch(e => console.error('Something failed!', e)
Plus, it makes no sense to use new Promise in getCustomer, just return the promise that comes from fetch:
function getCustomer(customer_id) {
const url = 'http://127.0.0.1:8000/customer-detail/' + customer_id+ '/';
return fetch(url)
.then(resp => resp.json())
.then((item) => resolve(item))
.catch(e => console.error('Something failed!', e));
}
But in general, I'd recommend looking into async/await, it would greatly clean up your code and make it much easier to read and understand for yourself:
async function test (pmt_id, crm_supplier_id) {
try {
const url = `http://127.0.0.1:8000/creditor-detail/${encodeURIComponent(pmt_id)}/supplier/${encodeURIComponent(crm_supplier_id}/`
const listResponse = await fetch(url)
if (listResponse.status !== 200) throw new Error(`List request failed with status ${indexResponse.status}`)
const listResult = await listResponse.json()
const results = await Promise.all(listResult.map(c => getCustomer(c.customer_id)))
console.log('All done', results)
} catch (err) {
console.log(err)
}
}
async function getCustomer (customer_id) {
const customerResponse = await fetch(`http://127.0.0.1:8000/customer-detail/${encodeURIComponent(customer_id)}/`)
if (customerResponse.status !== 200) throw new Error(`Customer request failed with status ${customerResponse.status}`)
const customerResult = await customerResponse.json()
return customerResult
}
I have my data going into a API in a loop as below. How can I ensure that the next iteration is called when the first is finished? Image my API takes a second for each element
saveMyData(){
this.elements.forEach(element => {
CALL_MY_API.read(element)
.then(response => {
// comes here in 1 second
// do something with response
}
.catch(error => {
this.showError(error)
})
})
}
This starts a bunch of asynchronous operations in parallel. I assume that's not what you want, and you want to wait for one to actually finish before starting the next one.
It's easiest if you can use async/await syntax:
async saveMyData() {
for (const element of this.elements) {
try {
const response = await CALL_MY_API.read(element)
// do something with response
} catch (error) {
this.showError(error)
}
}
}
If you can't, you'll need to chain the promises manually:
saveMyData() {
// Start with an immediately resolved promise.
let promise = new Promise((resolve, reject) => resolve())
// Add each element to the chain.
this.elements.forEach(element => {
promise = promise.then(() => {
return CALL_MY_API.read(element)
.then(response => {
// do something with response
})
.catch(error => {
this.showError(error)
})
})
})
}
I have a service to get a list from server. But in this list I need to call another service to return the logo img, the service return ok, but my list remains empty. What i'm did wrong ?
I tried to use async/await in both services
I tried to use a separate function to get the logos later, but my html don't change.
async getOpportunitiesByPage(_searchQueryAdvanced: any = 'active:true') {
this.listaOportunidades = await this._opportunities
.listaOportunidades(this.pageSize, this.currentPage, _searchQueryAdvanced)
.toPromise()
.then(result => {
this.totalSize = result['totalElements'];
return result['content'].map(async (opportunities: any) => {
opportunities.logoDesktopUrl = await this.getBrand(opportunities['brandsUuid']);
console.log(opportunities.logoDesktopUrl);
return { opportunities };
});
});
this.getTasks(this.totalSize);
}
No errors, just my html don't change.
in my
console.log(opportunities.logoDesktopUrl);
return undefined
but in the end return filled.
info:
Angular 7
server amazon aws.
await is used to wait for promise.
You should return promise from getBrand if you want to wait for it in getOpportunitiesByPage.
Change the getBrand function as following.
getBrand(brandsUuid): Observable<string> {
this.brandService.getById(brandsUuid).pipe(map(res => {
console.log(res.logoDesktopUrl); return res.logoDesktopUrl;
}))
}
Change opportunities.logoDesktopUrl = await this.getBrand(opportunities['brandsUuid']); to opportunities.logoDesktopUrl = await this.getBrand(opportunities['brandsUuid']).toPromise();
Please make sure you imported map from rxjs/operators.
At first,when you await, you should not use then.
At second, async/await runs only with Promises.
async getOpportunitiesByPage(_searchQueryAdvanced: any = 'active:true') {
const result = await this._opportunities
.listaOportunidades(this.pageSize, this.currentPage, _searchQueryAdvanced)
.toPromise();
this.totalSize = result['totalElements'];
this.listaOportunidades = result['content'].map(async (opportunities: any) => {
opportunities.logoDesktopUrl = await this.getBrand(opportunities['brandsUuid']);
console.log(opportunities.logoDesktopUrl);
return opportunities;
});
this.getTasks(this.totalSize);
}
getBrand(brandsUuid) {
return new Promise((resolve, reject) => {
this.brandService.getById(brandsUuid).subscribe(res => {
console.log(res.logoDesktopUrl);
return resolve(res.logoDesktopUrl);
}, err => {
return reject(err);
});
});
}
But, because rxjs is a used in Angular, you should use it instead of async/await :
getOpportunitiesByPage: void(_searchQueryAdanced: any = 'active:true') {
this._opportunities.listaOportunidades(this.pageSize, this.currentPage, _searchQueryAdvanced).pipe(
tap(result => {
// we do that here because the original result will be "lost" after the next 'flatMap' operation
this.totalSize = result['totalElements'];
}),
// first, we create an array of observables then flatten it with flatMap
flatMap(result => result['content'].map(opportunities => this.getBrand(opportunities['brandsUuid']).pipe(
// merge logoDesktopUrl into opportunities object
map(logoDesktopUrl => ({...opportunities, ...{logoDesktopUrl}}))
)
),
// then we make each observable of flattened array complete
mergeAll(),
// then we wait for each observable to complete and push each result in an array
toArray()
).subscribe(
opportunitiesWithLogoUrl => {
this.listaOportunidades = opportunitiesWithLogoUrl;
this.getTasks(this.totalSize);
}, err => console.log(err)
);
}
getBrand(brandsUuid): Observable<string> {
return this.brandService.getById(brandsUuid).pipe(
map(res => res.logoDesktopUrl)
);
}
Here is a working example on stackblittz
There might be a simpler way to do it but it runs :-)