使用 Promise.allSettled 捕获被拒绝承诺的请求响应代码

Capture request response code of rejected promises with Promise.allSettled

我正在使用 Promise.allSettled 调用一组 URL,我需要捕获被拒绝承诺的请求的响应代码。使用 Promise.allSettled 提供的 result.reason 的值不足以准确评估拒绝承诺的原因。我需要请求响应代码(400、500、429 等)。

到目前为止我有以下内容:

var response = await Promise.allSettled(urls.map(url => fetch(url)))
    .then(results => {
        var data = [];
        results.forEach((result, num) => {
            var item = {
                'req_url': urls[num],
                'result': result.status,
                'result_details': result
            };
            data.push(item);
        });
        return data;
    });

我如何捕获被拒绝承诺的请求的响应代码并将其作为 属性 添加到返回的数组中?返回的数组应该看起来像这样:

[{
    'req_url': 'https://myurl.xyz/a',
    'req_status_code': 400,
    'result': 'rejected',
    'result_details': {
        'status': 'rejected',
        'message': 'TypeError: Failed to fetch at <anonymous>:1:876'
    }
},
{
    'req_url': 'https://myurl.xyz/b',
    'req_status_code': 419,
    'result': 'rejected',
    'result_details': {
        'status': 'rejected',
        'message': 'TypeError: Failed to fetch at <anonymous>:1:890'
    }
},
{
    'req_url': 'https://myurl.xyz/c',
    'req_status_code': 429,
    'result': 'rejected',
    'result_details': {
        'status': 'rejected',
        'message': 'TypeError: Failed to fetch at <anonymous>:1:925'
    }
}]

有什么想法吗?

fetch 不会拒绝它对 HTTP 失败的承诺,只有 network 失败。 (在我看来 API 脚枪,I wrote up 几年前在我贫血的旧博客上。)我通常通过将 fetch 包装在 做的事情中来解决这个问题 拒绝 HTTP 失败。您也可以这样做,并在拒绝原因中提供失败状态。 (但您不必这样做,请参阅下文。)

class FetchError extends Error {
    constructor(status) {
        super(`HTTP error ${status}`);
        this.status = status;
    }
}
async function goFetch(url, init) {
    const response = await fetch(url, init);
    if (!response.ok) {
        // HTTP error
        throw new FetchError(response.status);
    }
    return response;
}

然后你可以将一个 async 函数传递给 map 以在本地处理错误,并使用 Promise.all (只是因为在一个地方做这一切比在两个地方做更简单Promise.allSettled):

const results = await Promise.all(urls.map(async url => {
    try {
        const response = await goFetch(url);
        // ...you might read the response body here via `text()` or `json()`, etc...
        return {
            req_url: url,
            result: "fulfilled",
            result_details: /*...you might use the response body here...*/,
        };
    } catch (error) {
        return {
            req_url: url,
            result: "rejected",
            result_status: error.status, // Will be `undefined` if not an HTTP error
            message: error.message,
        };
    }
}));

或者你可以在没有 fetch 包装器的情况下做到这一点:

const results = await Promise.all(urls.map(async url => {
    try {
        const response = await fetch(url);
        if (!response.ok) {
            // Local throw; if it weren't, I'd use Error or a subclass
            throw {status: response.status, message: `HTTP error ${response.status}`};
        }
        // ...you might read the response body here via `text()` or `json()`, etc...
        return {
            req_url: url,
            result: "fulfilled",
            result_details: /*...you might use the response body here...*/,
        };
    } catch (error) {
        return {
            req_url: url,
            result: "rejected",
            result_status: error.status, // Will be `undefined` if not an HTTP error
            message: error.message,
        };
    }
}));