简体   繁体   中英

Await a promise multiple times in parallel

I have some server API methods I call during startup of my application at different places, which can't be refactored in terms of where they are called, let's say getSettings() and getSchedule() . Both methods are based on a login(): Promise<Account> method as settings as well as the schedule are user-based.

Now I have solved the beginning of getSettings() and getSchedule() like the following:

class UserFunctions {
    private fetch_login: Promise<AccountItem> = undefined;
    async getSchedule(): Promise<any> {
        var account = getAccount();
        if (!account) {
            // No account loaded
            let isLogginIn = this.fetch_login;
            if (!this.fetch_login) {
                // Not logging in from any parallel method
                this.fetch_login = login();
            }
            account = await this.fetch_login;
            this.fetch_login = undefined;
        }

        // Now get schedule...
    }
}

The idea behind is that the login() function is only called once no matter how often it could be called. That's why I keep a reference on the Promise to await it multiple times. This works, but I noticed that sometimes when login() is done getSettings() gets earlier the okay to continue work and getSchedule() stays a few seconds until it continues executing. Sometimes it's the other way around and sometimes both methods return in the same time.

Here I have a print of the output:

06-05 16:46:08.126 27376 27397 I ReactNativeJS: Logging in back

06-05 16:46:08.690 27376 27397 I ReactNativeJS: Logged in back

06-05 16:46:08.696 27376 27397 I ReactNativeJS: Schedule downloaded

06-05 16:46:09.274 27376 27397 I ReactNativeJS: Logged in back

Do you have any idea how the code can be improved that once login() is done, both methods continue working?

I think you've got the right idea, but the logic needs a little tweaking:

    var account = getAccount();
    if (!account) {
        // No account loaded            
        if (!this.fetch_login) {
            // Not logging in from any parallel method
            this.fetch_login = login();
        }
        account = await this.fetch_login;
    }

Basically the idea is that you'll set fetch_login to have the value of the promise the first time it gets called. After that, you can await that same promise as many times as you need to.

Two things:

  1. You're calling fetch_login . You should just be referring to it:

     account = await this.fetch_login; // <== No () 
  2. Remove this line:

     this.fetch_login = undefined; 

Eg:

async getSchedule(): Promise<any> {
    var account = getAccount();
    if (!account) {
        // No account loaded
        if (!this.fetch_login) {
            // Not logging in from any parallel method
            this.fetch_login = login();
        }
        account = await this.fetch_login;
    }

    // Now get schedule...
}

You keep the promise resulting from the login attempt, so you can await it each time. (Nothing about await ing a promise prevents your doing so again.)


It seems like this should be handled at the level of getAccount and login , though, rather than within UserFunctions . getAccount and login appear to be global (within your app or module or whatever). I would probably have them manage this, with getAccount returning a promise:

var accountPromise = null;

function getAccount() {
    if (!accountPromise) {
        accountPromise = login();
    }
    return accountPromise;
}

then

async getSchedule(): Promise<any> {
    var account = await getAccount();

    // Now get schedule...
}

Example:

 var accountPromise = null; function getAccount() { if (!accountPromise) { accountPromise = login(); } return accountPromise; } function login() { return new Promise(resolve => { console.log("Logging in..."); setTimeout(() => { console.log("Logged in"); resolve({}); // {} = the account }, 800); }); } class UserFunctions { /*private*/ fetch_login/*: Promise<AccountItem>*/ = undefined; async getSchedule()/*: Promise<any>*/ { var account = await getAccount(); // Now get schedule... console.log("Got account, getting schedule"); } } // The first time it's used const u1 = new UserFunctions(); u1.getSchedule(); // Logs in and gets schedule // The second time it's used setTimeout(() => { const u2 = new UserFunctions(); u2.getSchedule(); // Gets schedule (already logged in) }, 2000); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM