Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript - Wait for promise resolve before function return

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!

like image 202
EdR Avatar asked Apr 18 '18 18:04

EdR


People also ask

Does await wait for promise to resolve?

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.

How do you wait for a promise to resolve node?

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.

Is promise resolve same as return?

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.

How do you resolve promises after 2 seconds?

Use setTimeout() to call the promise's resolve function with the passed value after the specified delay .


2 Answers

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.

like image 157
kingdaro Avatar answered Oct 04 '22 09:10

kingdaro


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);        
    });
    }
}
like image 43
Reza Avatar answered Oct 04 '22 08:10

Reza