Updated with working solution
I start with a function that grabs information from all rows selected in a table and pushes that information into a stack for later processing.
for (var i = 0; i < this.selectedItems().length; i++) {
var row = this.selectedItems()[i];
let info = this.createFileReqInfo(row.Number(), FileRequestType.AssociatedDoc);
fileReqInfo.push(info);
}
My Problem: The createFileReqInfo
function returns before the asynchronous API call can return a value, so I'm not getting the right value for userCanView in the return object.
createFileReqInfo = (reportId: number, fileRequestType: FileRequestType) : any => {
let fileReq = new FileRequest(reportId, fileRequestType);
var uCanView = false;
this.reportModel.getReportSecurity(reportId).done((result) => {
uCanView = result.CanViewReport;
var info: {
fileRequest: FileRequest,
userCanView: boolean
} = {
fileRequest: fileReq,
userCanView: uCanView
}
return info;
});
}
The compiler won't let me use await
here. Any ideas on how to get the function to block until the API call returns?
Solution
My problem here is that there were a few levels of function calls here that I had to rework to create and resolve promises. The function that calls the API resembles something in the suggested solutions
createFileReqInfo = (reportId: number, fileRequestType: FileRequestType) : JQueryPromise<any> => {
let fileReq = new FileRequest(reportId, fileRequestType);
var uCanView = false;
var info: {
fileRequest: FileRequest,
userCanView: boolean
} = {
fileRequest: fileReq,
userCanView: uCanView
}
let dfd: JQueryDeferred<any> = $.Deferred();
this.reportModel.getReportSecurity(reportId).done((result) => {
uCanView = result.CanViewReport;
info.userCanView = uCanView;
dfd.resolve(info);
});
return dfd;
}
This now returns a promise. The problem I had is with the function that calls this one because it iterates through a list of selected items and queues up various reports to download, but only the ones that exist in that particular selection. Had to use this answer to figure out a way to wait for all the promises to resolve before continuing.
getFileReqsFromSelectedItems = (view1: boolean, view2: boolean, view3: boolean): JQueryPromise<any> => {
var fileReqInfo: {
fileRequest: FileRequest,
userCanView: boolean
}[] = [];
let dfd: JQueryDeferred<any> = $.Deferred();
let promise: JQueryPromise<any>;
let promiseArray: JQueryPromise<any>[] = [];
for (var i = 0; i < this.selectedItems().length; i++) {
var row = this.selectedItems()[i];
if( view1 && row.HasView1() ) {
promise = this.createFileReqInfo(row.Number(), FileRequestType.AssociatedDoc1);
promiseArray.push(promise);
}
if( view2 && row.HasView2() ) {
promise = this.createFileReqInfo(row.Number(), FileRequestType.AssociatedDoc2);
promiseArray.push(promise);
}
if( view3 && row.HasView3() ) {
promise = this.createFileReqInfo(row.Number(), FileRequestType.AssociatedDoc3);
promiseArray.push(promise);
}
}
$.when.apply($, promiseArray).done(function() {
var promises = arguments;
for (var j = 0; j < promises.length; j++)
{
fileReqInfo.push(promises[j]);
}
dfd.resolve(fileReqInfo);
});
return dfd;
}
After that, it was easy to take the array of returned values and pass it to the download function.
downloadReports = () => {
this.getFileReqsFromSelectedItems(this.view1Check(), this.view2Check(), this.view3Check()).then((fileReqsDetails) => {
this.downloadTrialFiles(fileReqsDetails);
});
}
Whew!
The syntax: // works only inside async functions let value = await promise; The keyword await makes JavaScript wait until that promise settles and returns its result.
The async keyword before a function declaration marks it as asynchronous, and within it we can use the await keyword to wait for a promise to resolve. To wait for our promise to resolve and get the resolution as a return value from it, we just use the await keyword in-front of the promise.
The Promise.resolve() method "resolves" a given value to a Promise . If the value is a promise, that promise is returned; if the value is a thenable, Promise.resolve() will call the then() method with two callbacks it prepared; otherwise the returned promise will be fulfilled with the value.
Use setTimeout() to call the promise's resolve function with the passed value after the specified delay .
For APIs that don't support promises, you can use new Promise
to create a promise yourself, call resolve
with the result of the async call, then you can use async/await syntax when calling it. More info on promises
interface ReportSecurityInfo {
fileRequest: FileRequest
userCanView: boolean
}
createFileReqInfo = (reportId: number, fileRequestType: FileRequestType): Promise<any> => {
let fileReq = new FileRequest(reportId, fileRequestType);
var uCanView = false;
return new Promise((resolve, reject) => {
this.reportModel.getReportSecurity(reportId).done((result) => {
uCanView = result.CanViewReport;
var info: ReportSecurityInfo = {
fileRequest: fileReq,
userCanView: uCanView
}
resolve(info)
});
})
}
// somewhere else...
const info = await this.createFileReqInfo()
I also went ahead and split out the type of info
as its own interface for readability.
You can return promise from createFileReqInfo , then wait until it resolves
for (var i = 0; i < this.selectedItems().length; i++) {
var row = this.selectedItems()[i];
let info = await this.createFileReqInfo(row.Number(), FileRequestType.AssociatedDoc);
fileReqInfo.push(info);
}
Or
for (var i = 0; i < this.selectedItems().length; i++) {
var row = this.selectedItems()[i];
this.createFileReqInfo(row.Number(), FileRequestType.AssociatedDoc)
.then(info => {
fileReqInfo.push(info);
});
}
Where createFileReqInfo
is defined as:
createFileReqInfo = (reportId: number, fileRequestType: FileRequestType) : Promise<any> => {
return new Promise( (resolve, reject) => {
let fileReq = new FileRequest(reportId, fileRequestType);
var uCanView = false;
this.reportModel.getReportSecurity(reportId).done((result) => {
uCanView = result.CanViewReport;
var info: {
fileRequest: FileRequest,
userCanView: boolean
} = {
fileRequest: fileReq,
userCanView: uCanView
}
resolve(info);
});
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With