将异步函数返回的承诺类型更改为其他类型

Change type of promise returned by async functions to a different type

编辑 - 虽然答案包含几个可行的解决方案,但我要指出的是,Parse JS SDK 2.0(以及 Parse-Server 3.0)已经公布,并且实际上删除了 Parse.Promise。因此,最好的解决方案是换掉 Parse Promise 实现并改用本机 Promises。

我使用 Parse-Server(最新版本,2.8.2)作为后端。我已经习惯了承诺,它们在我的代码中根深蒂固。

不过,我已经开始改用 async / await 模式。但它不能很好地与我现有的所有实现一起使用。

这个例子会抛出一个错误:

const promiseErrorTestHelper = async () => {
    return Parse.Promise.as();
}

Parse.Cloud.define('promiseErrorTest', async(req, res) => {
    promiseErrorTestHelper().always(
        res.success
    );
});

这个很好用:

const promiseErrorTestHelper = async () => {
    return Parse.Promise.as();
}

Parse.Cloud.define('promiseErrorTest', async(req, res) => {
    promiseErrorTestHelper().then(
        res.success
    );
});

在 Parse-Server 中,.always() 用于传递回调函数,无论承诺是被拒绝还是已解决。我在我的整个代码中都使用了它,并在重构一些函数以在添加新功能时使用 async / await 时发现了这个问题。

我理解问题在于异步函数将其结果包装在一个承诺中。因此,它将我的 Parse.Promise 转换为没有 .always() 方法的不同类型。

是否有一种简单的方法可以让我将异步功能覆盖为 return 和 Parse.Promise?或者,如果我想使用异步/等待,我是否必须重新处理 always() 调用?

您似乎无法更改 return 从 async 函数编辑的承诺类型。它内置于 return 原生承诺,不可配置。关于如何使 async 函数 return 成为 Bluebird 承诺 here,这里有类似的讨论。结论是你不能,所以如果你想要一个 Bluebird 承诺,你必须包装 async 函数。


要在错误后继续,通常的方法是使用 .catch() 记录错误并且不会重新抛出,因此 "handling" 错误:

someFunc().catch(err => {
    // handle the error, continue processing, changes promise chain to resolved
    console.log(err);
    return null;
}).then(() => {
    // will always get called when original promise resolves or rejects
    // somewhat like your .always
});

或者,您可以将异步函数包装到 return 您想要的承诺类型:

function wrapFunc(arg) {
    return Parse.Promise.resolve(someAsyncFunc(arg))
}

然后调用包装函数。


我不确定我是否推荐这个,因为您正在修改全局可访问的 Promise 原型,这可能会影响其他代码,但您也可以向内置 promise 添加 .always()

Promise.prototype.always = function(fn) {
    return this.then(fn, fn);
}

这实际上是完全相同的实现 that Parse uses。然后,您可以在从 async 函数 return 编辑的承诺上使用 .always()

jfriend00 的回答是一个可以接受的解决方案。 .catch(value => {return value}).then(...) 基本上将作为 .always() 发挥作用。

就我而言,我不想这样做,因为这意味着我要在许多地方修补代码以解决源自一个地方的问题。我正在重新设计一个现有的函数,该函数使用 Parse.Promises 来使用 async / await,我也向它添加了一些功能。直到完成后我才发现 .always() 问题。我不想简单地扔掉返工并承诺再次重做更新。

虽然草率,但我最终将我的辅助函数包装在它自己的辅助函数中。原始的辅助函数 不是 异步的,return 是一个基于异步辅助-辅助函数的结果解决的承诺。

const promiseErrorTestHelper = async (<same params>) => {
    let returnPromise = new Parse.Promise();

    promiseErrorTestHelperHelper(<same params>).then(
        success => { returnPromise.resolve(success); },
        error => { returnPromise.reject(error); }
    );

    return returnPromise;
}

const promiseErrorTestHelperHelper = async () => {
    // This can return whatever, body and parameters
    // copied in entirety from parent function
}

Parse.Cloud.define('promiseErrorTest', async(req, res) => {
    promiseErrorTestHelper().then(
        res.success
    );
});

这使我能够像以前一样调用原始的辅助函数,尽管更新后的实现更加清晰易读。

不过,有一件事是我无法使用函数式编程,我最近一直在学习并尝试付诸实践。这没有用:

const promiseErrorTestHelper = async (<same params>) => {
    let returnPromise = new Parse.Promise();

    promiseErrorTestHelperHelper(<same params>).then(
        returnPromise.resolve,
        returnPromise.reject
    );

    return returnPromise;
}

我的理解是,这应该与上面的几乎相同,只是没有被包裹在匿名函数中。当然,变量没有命名,但应该以任何一种方式传递给那些方法,对吧?我在别处使用了这个模式,虽然不是 promise.resolve / reject 因为我只是直接 return 。但是我有很多 .then( response.success, response.error ) 调用我的云函数,而不是做我在上面的工作示例中所做的 returnPromise.resolveresponse.success 交换和 returnPromise.reject 与 [= 交换20=]。那些工作正常。