【问题标题】:async in JavaScript - When to use await (and when to not) - 'await' has no effect on the type of this expression .ts(80007)JavaScript 中的异步 - 何时使用 await(以及何时不使用) - 'await' 对此表达式的类型没有影响 .ts(80007)
【发布时间】:2021-06-07 16:24:21
【问题描述】:

我在我的代码中经常看到这个问题,并且我在这里看到过类似的帖子,但它们似乎与我的问题没有特别的联系,这让我觉得我在某个地方遗漏了一些东西。

注释'await' has no effect on the type of this expression.ts(80007) 出现在下面的异步方法中(作为示例)...

    /**
     * Deletes the account for the currently logged in user.
     */
    async deleteAccount() {
        Logger.log('Deleting the currently logged in user', 'Auth')

        const service = ServiceFactory.profile()
        const operationState = await service.deleteUser()

        if (!operationState.succeeded)
            operationState.throw()
    },

通过堆栈,通过如下方式调用ProfileService类中的该方法,该类在ServiceFactory.profile()中返回...

    /**
     * Deletes a user.
     * @returns {ServiceOperation} The operation result.
     * @memberof ProfileService
     */
    async deleteUser() {
        const operationState = new ServiceOperation('Delete User', true)
        const url = `${ this.baseUrl }?SchemeId=${ this.schemeId }`
        Logger.log(url, 'Request [DELETE]')

        try {
            return this.processResponse(await Axios.delete(url), operationState, 'deleteUser')
        } catch (err) {
            return this.failOperation(err, operationState, 'deleteUser')
        }
    }

该问题似乎与基类上的 processResponse 方法有关,该方法将我的 JSON 响应从后端转换为 ServiceOperation 类,该类跟踪运行状态、成功、合并多个结果并将不同类型的错误响应转换为与应用程序处理一致 - 此处添加的代码在很大程度上无关紧要......

    /**
     * Handles a succesfull data changing operation and processes the response safely.
     *
     * @param {Object} response The service response.
     * @param {ServiceOperation} operationState
     * @param {string} [methodName='?'] The name of the calling method.
     * @returns {ServiceOperation} The modified operation state.
     *
     * @memberof Service
     */
    processResponse(response, operationState, methodName = '?') {
        if (response.data !== false && !response.data || response.data == null) {
            operationState.complete(true)
        } else {
            operationState.complete(true, response.data)
        }
            Logger.logObject(response, `Operation completed [${ methodName }]`, this.loggingCategory)

        return operationState
    }

failOperation 只是processResponse 失败调用方法...

    /**
     * Handles a failed operation.
     *
     * @param {Error} error
     * @param {ServiceOperation} operationState
     * @param {string} [methodName='?'] The name of the calling method.
     * @returns {ServiceOperation} The modified operation state.
     *
     * @memberof Service
     */
    failOperation(error, operationState, methodName = '?') {
        operationState.fail(error)
        Logger.logObject(operationState, `Data service failed [${ methodName }]`, this.loggingCategory)
        return operationState
    }

上述两个方法都是Service 类的一部分,该类被扩展为之前包含deleteUser 函数的ProfileService 类(Service 类充当每个应用中的服务类。

我在这里有点困惑的是processResponse 不是async(也不需要是)。等待的调用在调用它的方法中被等待。这只是编辑器没有捡起它的一个案例,还是我怀疑,当我处理数据时我失去了潜在的承诺 - 如果是这样的话,我如何才能让该方法浮出水面的承诺processResponse 需要的结果?

processResponse 是一种通用方法,可以处理我在我的应用程序中拥有的所有服务方法结果,因此合并这些方法是不可行的。我考虑过让processResponse 本身为async 并在其上使用await,但从逻辑上讲,这似乎完全不会做任何事情,因为没有什么可以真正等待。

从功能上看,应用程序代码似乎工作正常,所以我不愿意在这个级别开始将它拆开,直到我完全理解它为什么会抱怨。

额外(但可能无关)信息

这是Service 类及其派生类使用的ServiceOperation 类的定义。这仅用于完成,并且在很大程度上无关紧要。

/**
 * Defines an operation performed through a service.
 *
 * @export
 * @class ServiceOperation
 */
export default class ServiceOperation {

    /**
     *Creates an instance of ServiceOperation.
     * @param {string} name
     * @param {boolean} [started=false]
     * @memberof ServiceOperation
     */
    constructor(name, started = false) {
        this.name = name
        this.running = started
        this.completed = false
        this.succeeded = false
    }

    /**
     * Completes a service operation.
     *
     * @param {boolean} succeeded
     * @param {object} data Any data or error info returned
     * @returns {ServiceOperation} itself.
     * @memberof ServiceOperation
     */
    complete(succeeded, data) {
        this.running = false
        this.completed = true
        this.succeeded = succeeded

        if (data !== undefined && data !== null || this.data !== undefined && this.data !== null)
            this.data = data

        return this
    }

    /**
     * Fails a service operation.
     *
     * @param {string} error An error
     * @returns {ServiceOperation} itself.
     * @memberof ServiceOperation
     */
    fail(error) {
        this.complete(false)

        if (error && error.response && error.response.data) {
            this.data = error.response.data
            this.errorObject = error
            this.error = error.response.data.message ? error.response.data.message : error.message
            this.errorObject.message = this.error
        } else {
            this.error = error && error.message ? error.message : error
            this.errorObject = error
        }

        return this
    }

    /**
     * Throws the wrapped error object back for interrogation.
     *
     * @memberof ServiceOperation
     */
    throw() {
        if (this.errorObject)
            throw this.errorObject
        else if (this.error)
            throw new Error(this.error)
        else
            throw new Error('Service Operation error')
    }

    /**
     * Merges another operation into this one, updating it's info.
     *
     * @param {ServiceOperation} operation
     * @param {boolean} includeData If not set then the data item will be eradicated if it is present.
     * @memberof ServiceOperation
     */
    updateMergeFrom(operation, includeData = false) {
        this.running = operation.running
        this.completed = operation.completed
        this.succeeded = operation.succeeded

        if (!includeData && this.data)
            delete this.data
        else if (includeData && operation.data)
            this.data = operation.data


        if (operation.stillPending !== undefined)
            this.stillPending = operation.stillPending

        if (!this.error && operation.error)
            this.error = operation.error
    }

    /**
     * Generates an immediately completed operation.
     *
     * @static
     * @type {ServiceOperation}
     * @memberof ServiceOperation
     */
    static get immediate() {
        return new ServiceOperation('Immediate').complete(true)
    }
}

【问题讨论】:

  • service.forgotPassword 是否返回承诺?如果没有,那么await-ing 它大多是多余的。我不确定你的三段代码是如何联系起来的。
  • 另外,我觉得有点奇怪,显然forgottenPassword 调用最终调用deleteUser。从一般角度来看似乎不正确。
  • async/await 只是 Promises 语法糖的关键字。 service.forgotPassword 是否返回承诺?
  • 啊 - 我似乎患有复制/粘贴失明 - 哦,亲爱的......
  • 已更新以将 forgottenPassword 替换为 deleteUser - 我有很多方法存在相同的问题,因此我认为 processResponse 在某种程度上是问题。

标签: javascript ecmascript-6 async-await


【解决方案1】:

我通过这篇文章找到了这个问题的答案......

https://github.com/microsoft/TypeScript/issues/34508

这似乎是 TypeScript 编译器生成的警告(尽管我的项目甚至没有使用 TypeScript,所以 - {shrug})

为了让它消失,您需要编辑您的 JSDoc 条目,如下所示...

    /**
     * Deletes a user.
     * @returns {ServiceOperation} The operation result.
     * @memberof ProfileService
     */
    async deleteUser() { }

变成

    /**
     * Deletes a user.
     * @returns {Promise<ServiceOperation>} The operation result.
     * @memberof ProfileService
     */
    async deleteUser() { }

感谢 @Bergi 让我的大脑找到正确的位置。

【讨论】:

    猜你喜欢
    • 2020-06-07
    • 2018-10-27
    • 1970-01-01
    • 2020-05-26
    • 1970-01-01
    • 1970-01-01
    • 2018-05-22
    • 1970-01-01
    • 2020-02-26
    相关资源
    最近更新 更多