【问题标题】:JavaScript async/await policy, function resolves early / await doesn't waitJavaScript 异步/等待策略,函数提前解析/等待不等待
【发布时间】:2020-12-30 15:39:52
【问题描述】:

我一直对这个概念的解释有点好奇。 我知道我只是误解了这一点。 下面是 2 个函数,第一个函数是为了等待另一个函数完成,然后才能解析为 true 或 false,但第二个函数会提前解析为 undefined。

async function authorizeAccount() {
        if (await authHandler.onLogin()) {
                <!-- removed for brevity -->
        }
        else {
            alert("Something went wrong during authorization. Try again.")
        }
    }
    
    async onLogin() {
        try {
            const result = await authorize(this.spotifyAuthConfig);
            if (result.authorizationCode) {
                axios.get('http://10.0.2.2:8000/authorizespotify/' +        result.authorizationCode)
                    .then((result) => {
                        //Set token expiration date time
                        result.data.tokenExpirationDate = Date.now() + (result.data.expires_in * 1000);
                        asyncStorageHandler.storeObject('tokenObject', result);
                        return true;
                    })
            }
            else {
                return false;
            }

        } catch (error) {
            return false;
        }
    }

有人可以解释为什么这个函数 onLogin 立即解析导致 if 语句解析“未定义”吗? 为什么以下相同功能的早期版本有效?显然,我们在这里等到 onLogin 函数解析为 true/false,然后再继续使用 authorizeAccount 函数。

async onLogin() {
        try {
            const result = await authorize(this.spotifyAuthConfig);
            if (result.accessToken) {
                await asyncStorageHandler.storeObject('tokenObject', result);
                return true;
            }
            else {
                return false;
            }

        } catch (error) {
        }
    }

我应该提一下,在新版本中,所有代码都运行了,我得到了令牌等,最终返回 true,但问题是此时承诺已经解决。那么我该如何规避这种行为呢?

【问题讨论】:

  • 第一个 sn-p 等待 authHandler.onLogin() 但没有 authHandler 因此条件立即评估为假。你的意思是if (await this.onLogin()),也许吧?
  • 旁注:使用标志将拒绝/错误转换为履行/返回通常是一种反模式。在大多数情况下,调用代码应该知道检查授权的尝试失败,而不是返回否定结果。
  • 第二个sn-p是老版本,因为要存储一个client secret server端,我重写了函数。是的,对不起,我忽略了一个事实,即这是在使用类和组件的 react 本机项目中,所以一切都井井有条。如前所述,我确实得到了令牌和所有这些,该函数只是提前解决了。
  • axios.get 是异步的。但是您并没有等待它,也没有在您的第一个 sn-p 中从 onLogin 返回任何内容。你必须做return axios.get(...
  • derpirscher。我懂了。但我想在解析之前在同一个函数中处理我所有的令牌逻辑。有没有办法做到这一点?

标签: javascript asynchronous ecmascript-6 async-await


【解决方案1】:

有人可以解释为什么这个函数 onLogin 立即解析导致“未定义”

因为您没有使用await 或类似方法来等待来自axios 调用(更准确地说,来自您的then 处理程序)的承诺完成。没有任何东西将来自axios.get().then() 的承诺与onLoad 隐式创建的承诺联系起来,因此后者不会等待前者解决。

为什么相同功能的以下早期版本有效?

因为它没有axios 调用,并且确实在我认为是异步操作(给定名称asyncStorageHandler)上有await。 :-)

绝对最小的改变是添加一个return,我在下面标记了***,但你也没有等待asyncStorageHandler的承诺,所以我已经也标记了这一点:

// >>> I do NOT recommend mixing things this way, keep reading <<<
async function authorizeAccount() {
    if (await authHandler.onLogin()) {
            <!-- removed for brevity -->
    }
    else {
        alert("Something went wrong during authorization. Try again.")
    }
}

async onLogin() {
    try {
        const result = await authorize(this.spotifyAuthConfig);
        if (result.authorizationCode) {
            return axios.get('http://10.0.2.2:8000/authorizespotify/' +        result.authorizationCode)
// *** −−−−−^^^^^^
                .then((result) => {
                    //Set token expiration date time
                    result.data.tokenExpirationDate = Date.now() + (result.data.expires_in * 1000);
                    return asyncStorageHandler.storeObject('tokenObject', result);
// *** −−−−−−−−−−−−−^^^^^^
                })
                .then(() => {    // *** Broke this out into its own
                    return true; // handler since we need to wait
                })               // for `asyncStorageHandler.storeObject`
        }
        else {
            return false;
        }

    } catch (error) {
        return false;
    }
}

这使得来自async 函数的promise 解析为来自axios.get().then() 的promise,所以最终来自axios.get().then() 的promise 会发生在来自async 函数的promise 上。没有那个return,没有任何东西可以将async函数的promise与axios.get().then()的promise联系起来。

但总的来说,最好不要混合使用await.then 处理程序(尽管偶尔会有例外):

async function authorizeAccount() {
    if (await authHandler.onLogin()) {
            <!-- removed for brevity -->
    }
    else {
        alert("Something went wrong during authorization. Try again.")
    }
}

async onLogin() {
    try {
        const result = await authorize(this.spotifyAuthConfig);
        if (result.authorizationCode) {
            const tokenResult = await axios.get('http://10.0.2.2:8000/authorizespotify/' +        result.authorizationCode)
// −−−−−−−−−^^^^^^^^^^^^^^^^^^^^^^^^^
            tokenResult.data.tokenExpirationDate = Date.now() + (tokenResult.data.expires_in * 1000); // *** changed result to tokenResult
            await asyncStorageHandler.storeObject('tokenObject', tokenResult);                              // *** "
            return true;
        }
        else {
            return false;
        }

    } catch (error) {
        return false;
    }
}

或者当然,根本不使用async函数,使用.then等:

onLogin() {
    return authorize(this.spotifyAuthConfig)
    .then(result => {
        if (result.authorizationCode) {
            return axios.get('http://10.0.2.2:8000/authorizespotify/' + result.authorizationCode)
            .then(tokenResult => {
                tokenResult.data.tokenExpirationDate = Date.now() + (tokenResult.data.expires_in * 1000);
                return asyncStorageHandler.storeObject('tokenObject', tokenResult);
            })
            .then(() => {
                return true;
            });
        }
        else {
            return false;
        }
    })
    .catch(error => {
        return false;
    });
}

...但是我们有async/await 的原因是使用这样的显式处理程序会变得很麻烦。

【讨论】:

  • 感谢您的回答,但是当我说 await 这个词只是将所有内容放在 .then() 语句中的语法糖时,我是否正确?我会重写它,但我不认为这是问题的根源。 (为了清楚起见,我确实尝试过重写它。)
  • @JoachimBülow - 是也不是。 async/await 是消费承诺的语法糖,是的(但是...good 糖)。您的代码的问题在于您没有等待承诺解决。我修复它的方法是使用await(因为这是在async 函数中)。 other 修复它的方法是使用.then 处理程序 awaitreturn 其返回值。但是如果你要使用.then 等,首先不要使用async 函数。将两者混合使用会使您自己和阅读代码的其他人感到困惑。 :-)
  • 不,你不正确。至少在那种情况下不会。 axios.get 是一个全新的promise,与onLogin 调回的promise 无关。而且因为在您的情况下,您没有从 onLogin 返回任何内容,所以它的承诺会以未定义的方式解决。来自 axios 的承诺稍后会解决,但您没有捕捉到它的结果。
  • @JoachimBülow - 我已经更新了答案,或许可以更清楚地说明为什么您的原件不起作用。 FWIW,如果您想更深入地了解 Promise 以及 async/await,我将在我最近的一本书 JavaScript: The New Toys 的第 8 章和第 9 章中深入介绍它们。如果您有兴趣,请在我的个人资料中链接。
  • 感谢您的帮助@T.J.克劳德,真的很感激。最后一切都解决了,我学到了很多。你得到了一个接受的答案:)
猜你喜欢
  • 2019-06-20
  • 2023-03-13
  • 2019-05-10
  • 1970-01-01
  • 2016-07-07
  • 2016-03-25
  • 2017-10-09
相关资源
最近更新 更多