javascript - 结契约(Contract)步代码和 promise 的正确方法是什么?

标签 javascript error-handling async-await es6-promise

我正在编写一个函数,用作实际服务器 API 调用和用户空间之间的 API 中介函数。
此函数验证一些输入数据(同步),将其转换为适当的(同步),读取一些其他数据(异步)。然后将结果合并并用于最终对服务器端点(异步)进行实际调用。
此函数必须始终返回一个 Promise,并且使用它的更高级别 API 中的所有执行流程都应通过 Promise 链式处理。

但是,我无法确定我是否以正确的方式进行操作。而且,我有一些相关的问题我找不到答案。

这是我到目前为止写的代码:

import axios from "axios";
import * as MyAPI from "./MyAPI";
import querystring from "querystring";

function fetchFromServerEndpoint(route, routeParams, optQueryParams) {
    // Is it ok to use Promise.all() to combine sync and async code as below?
    return Promise.all([
        new Promise((resolve, reject) => {
            const routeReplacedParams = route.replace(/{([^{/]*?)}/g, (match, paramName) => {
                const paramValue = routeParams[paramName];

                if (paramValue === undefined) {
                    // Here I need to terminate execution of the replacer function
                    // and reject this promise
                    // Is it ok to `throw` here?
                    throw "UNDEFINED_ROUTE_PARAMETER";

                    // Can I somehow `return reject(...)` here instead of throw-ing?
                }

                return paramValue;
            });

            return resolve(routeReplacedParams);
        }),
        // Is it fine to use async to mock a promise with synchronous code?
        async function(){
            return querystring.stringify(optQueryParams);
        }),
        // This is part of a custom API, returns a legit promise
        MyAPI.getAuthorizationAsync(),
    ]).then(([finalRoute, queryParams, authToken]) => {
        // axios library, all APIs will return a promise
        return axios.get(`${finalRoute}?${queryParams}`, {
            headers: {
                Authorization: `Basic ${authToken}`
            }
        });
    });
}

我的问题是:
  • 当从同步函数中出现错误情况时,停止执行代码块的正确方法是什么?此外,就像在 String.replace 的替换函数中一样,可以在那里“扔”吗?你能建议任何更好的选择,或者更适合 promise 的方法吗?
  • async 标记函数将隐式创建一个 promise ,并且还将隐式转换(未捕获)throw转入 Promise.reject() s 并返回介绍 Promise.resolve() s。好处是您可以编写与普通同步函数相同的代码,但仍使其行为类似于 Promise,因此您可以将 Promise 链接到它。我的这种理解不正确吗?是否有我应该注意的缺点(或好处,除了我刚才描述的)?
  • 我使用 Promise.all() 来组合函数执行的多个独立阶段的结果。我想这很好,但是还有其他方法可以处理这种情况吗?或者也许这是一个不好的做法?相反,我是否应该将 Promise 和从 Promise 到下一个 Promise 的传递结果链接起来,直到达到我需要使用所有 Promise 的级别?
  • 最佳答案

    What is a proper way to stop execution of a block of code when an error situation arises, from a syncrhonous function, inside a promise? Moreover, like in the replacer function of String.replace, is it ok to 'throw' there? Can you suggest any better option, or maybe, a more promise-friendly approach?



    扔在那里是可以的。 Promise executor 函数确实会捕获 executor 函数中发生的同步异常并将它们转换为被拒绝的 Promise。

    这是否是一件好事真的取决于你想要发生什么。如果您希望调用函数中的 promise 拒绝,那么您可以从同步函数中抛出,这将冒泡到异步调用者中的 promise 并导致拒绝。否则,您也可以自由使用常规同步技术,例如从同步函数返回错误条件,在调用者中检查该错误,然后采取相应措施。因此,与任何其他设计一样,它完全取决于您希望如何调用同步函数以及调用者应如何检查错误或处理错误。有时返回的错误代码是正确的,有时抛出异常是正确的。

    例如,如果调用 randomSyncFunctionThatMightThrow() throws,然后调用者的 promise 将被自动拒绝,.catch()处理程序将被击中。
    function randomSyncFunctionThatMightThrow() {
        // other logic
        throw new Error("random error");
    }
    
    someFuncThatReturnsPromise().then(arg => {
        // bunch of logic here
        randomSyncFunctionThatMightThrow();
        // other logic here
        return someThing;
    }).then(result => {
        console.log(finalResult);
    }).catch(err => {
        console.log(err);
    });
    

    Marking a function with async will implictly create a promise, and will also implictly convert (uncaught) throws into Promise.reject()s and returns intro Promise.resolve()s. The benefit is that you can write code identically to a normal synchronous function, but still have it behave like a promise, so you can chain promises to it. Is this understanding of mine incorrect?



    对我来说听起来很对。 async函数有 try/catch内置并自动捕获任何异常并将它们转变为被拒绝的 promise 。

    Are there drawbacks (or benefits, other than what I just described) that I should be aware of?



    使用 Promise 强制所有同步代码异步处理正常的普通返回结果有其局限性。由于明显的编码复杂性原因,您不会将它用于整个程序中的每个同步函数。异步代码的编写和测试更耗时,所以我不会去寻找让普通同步事物开始返回 promise 并让所有调用者异步处理的方法。有时,如果某些同步代码也处理 Promise,那么它可能更容易与异步代码混合。但是,这不是同步代码的普通用法。

    I used Promise.all() in order to combine the results of multiple, independent stages of the function execution. I suppose this is fine, but are there any other ways to deal with this situation? Or maybe it's a bad practice? Should I, instead, chain promises, and pass-through results from a promise to the next until, I reach the level where I need to use all of them?


    Promise.all()适用于您希望同时拥有多个独立且并行的异步执行链并且您只想知道它们何时全部完成并立即获得所有结果的情况。 Promise 链接适用于当您出于多种原因想要一个接一个地对事物进行排序时,但通常是因为第 2 步取决于第 1 步的结果。你应该选择Promise.all()与链接取决于异步代码的需要(并行执行与顺序执行)。

    将同步代码放入 Promise.all()可以正常工作(你甚至不必将它包装在一个 promise 中,因为 Promise.all() 也会接受运行函数的返回值),但通常没有意义。同步代码是阻塞和同步的,所以你通常不会通过将同步代码放在 Promise.all() 中来获得任何并行性。 .我通常会在 Promise.all() 之前或之后运行同步代码。而不是将它与实际的异步 promise 混合在一起。

    However, I am having trouble figuring out if I'm doing it the proper way.



    我在下面包含了一个替代实现。作为一般做法,我不喜欢将同步代码包装在 Promise 中,所以这是我的替代方案。我用过 async两者都起作用,所以我可以使用 await并且同步异常会自动转换为被拒绝的 promise ,并且你的函数的契约(Contract)已经返回了一个 promise ,所以这只是实现相同结果的一种更简单的方法(在我看来)。当然,这只是很大程度上受意见支配的编码风格。

    以下是我可能会如何编写您的代码:
    async function fetchFromServerEndpoint(route, routeParams, optQueryParams) {
        const finalRoute = route.replace(/{([^{/]*?)}/g, (match, paramName) => {
            const paramValue = routeParams[paramName];
    
            if (paramValue === undefined) {
                // Here I need to abort execution of the replacer function
                // parent promise will be rejected
                throw new Error("UNDEFINED_ROUTE_PARAMETER");
            }
            return paramValue;
        });
        const queryParms = querystring.stringify(optQueryParams);
        const authToken = await MyAPI.getAuthorizationAsync();
        return axios.get(`${finalRoute}?${queryParams}`, {
            headers: {
                Authorization: `Basic ${authToken}`
            }
        });
    }
    

    如果你真的不喜欢 await ,您可以通过将结尾更改为:
        return MyAPI.getAuthorizationAsync().then(authToken => {
            return axios.get(`${finalRoute}?${queryParams}`, {
                headers: {
                    Authorization: `Basic ${authToken}`
                }
            });
        });
    

    但是,正如我在其他地方的评论中所说,我认为 await是一个有用的工具(如果使用得当),可以让您编写更简单的代码。

    关于javascript - 结契约(Contract)步代码和 promise 的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61197607/

    相关文章:

    javascript - 如何在没有评估的情况下检测异步功能支持?

    javascript - 使用@keyframes 以不透明度淡出时,如何使用事件重复动画?

    c++ - 即使代码看起来正确,也没有创建第二个文件

    asp.net - 为特定路径禁用 "httpErrors"元素

    C# 在委托(delegate)调用中等待

    c# - 查找未等待的异步方法调用

    javascript - node.js 中的几个控制台实例

    javascript - 使用 ng-repeat 时如何获得元素的正确高度和偏移值?

    javascript - 消除渲染阻塞 JavaScript 和 CSS

    c++ - SQL Server 从扩展存储过程捕获错误