reqwest
reqwest copied to clipboard
Promises returned from .catch aren't honored
I'm calling an API recursively to build a tree. Sometimes, I get throttled and need to exponentially back-off. I can't do this with Reqwest natively, because anything I return from .catch
falls on the floor.
I was able to fix this by wrapping the reqwest
instance in Promise.resolve
, which gives me a Node-native Promise instead of Reqwest's thennable quasi-promise.
The solution looks like this:
function buildTree (itemNodes) {
itemNodes = Array.prototype.slice.apply(itemNodes);
return Promise.all(
Lazy(itemNodes).map(
function (itemNode, i, itemNodes) {
// Exponential back-off settings
var nextDelay = 1000;
var MAX_DELAY = 16000;
var attemptRequest = function () {
// Reqwest's .catch method won't let us save the promise when there's a
// recoverable error (like a 503); however, we can fall back to Node's
// implementation by wrapping the Reqwest in Promise.resolve
return Promise.resolve(
new Reqwest(
{
"url": // URL for this branch of the tree…
}
)
).then(
function (childNode) {
childNode = domify(childNode);
var childItemsNode = childNode.querySelector("category > items");
var childItemNodes = childNode.querySelectorAll("category > items > item");
// if childItemsNode is owned by another document, we can't append it to itemNode
childItemsNode._ownerDocument = null;
itemNode.appendChild(childItemsNode);
return buildTree(childItemNodes);
}
).catch(
function (response, message, error) {
// Exponentially back-off if we've been rate-limited
if (response.status == 503 && nextDelay <= MAX_DELAY) {
// Add a fraction of a second to the wait time to make sure parallel retries don't collide
return Promise.delay(nextDelay + 1000 * Math.random()).then(
function () {
nextDelay *= 2;
return attemptRequest();
}
);
} else {
if (error && error.stack) {
console.error(error.stack);
} else {
console.error("Error in buildTree:");
console.error({"url": response._url.href, "status": response.status, "content": response.response});
}
}
}
);
};
// …
return attemptRequest();
}
).compact().toArray()
).then(
// This was never called if the API failed before I wrapped with Promise.resolve
function () {
// Return the same data we received, after we've processed it
return itemNodes;
}
);
};
};