Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle request-promise errors properly?

So I'm using request-promise in a script that I have that loops through a list of urls and fires of requests. Then I want to do something w/ the data received once all the requests are complete.

I have the following:

var rp = require('request-promise');

rp.get({
    uri: 'http://httpstat.us/500',
    transform: function(body, res){
        res.data = JSON.parse(body);
        return res;
    }
}).then(function(res){
    results.push(res.data);
})
.catch(function(err){
    should.throw.error.to.console();
    var respErr  = JSON.parse(err.error);
    var errorResult = {
        origUrl: respErr.origUrl,
        error: respErr
    };
    results.push(errorResult);
});

As you can see.. http://httpstat.us/500 throws a 500, which causes the .catch() block to be ran. I'm forcing an error. should.throw.error.to.console(); should throw an error to the console, but instead, the script just exits silently w/o any error code (Process finished with exit code 0).

I'm assuming that request-promise is catching the error from node's http when a page doesn't come back w/ 2xx code and then passing that back to the catch() callback. But any subsequent errors then end up failing silently. How in the world do I handle this so that the rest of my code will still throw errors properly?

Related GitHub issue

like image 782
RavenHursT Avatar asked May 30 '15 00:05

RavenHursT


People also ask

How do you handle errors in Promise chains?

There are two ways in which you can handle errors in your promise chain, either by passing an error handler to then block or using the catch operator.

How do you handle Promise reject error?

We must always add a catch() , otherwise promises will silently fail. In this case, if thePromise is rejected, the execution jumps directly to the catch() method. You can add the catch() method in the middle of two then() methods, but you will not be able to break the chain when something bad happens.

How do you handle a Promise response?

To support error handling, Promise objects provide a catch() method. This is a lot like then() : you call it and pass in a handler function. However, while the handler passed to then() is called when the asynchronous operation succeeds, the handler passed to catch() is called when the asynchronous operation fails.

What happens if one request fails in Promise all?

Promise.all fail-fast behaviorPromise.all is rejected if any of the elements are rejected. For example, if you pass in four promises that resolve after a timeout and one promise that rejects immediately, then Promise.all will reject immediately.


1 Answers

What do you mean by "any subsequent errors then end up failing silently"? If the original promise rp fails, the catch executes… at the time of failure. Once a promise is rejected, that's it, there can be no "subsequent errors."

Also, should looks like an assertion (e.g. from chai) which would suggest that you are trying to test this. Chai's should.throw doesn't throw an error, it checks that an error has been thrown. If you are testing this, you need to indicate to the test (it block) that the test is async, not sync — usually by naming & invoking a done parameter. Otherwise, the request will be sent out, and then before ANY response can be made, the script will synchronously end and no errors will be listened for.

What's more, you are spec'ing that something should throw to console, but nothing in your code throws! If you DID write in a throw, you should understand that throw inside a then or catch will simply cause the outgoing promise from that handler to be rejected with the thrown value (yes, catch exports a new promise, just like then — it is 100% sugar for .then(null, errHandler). If you want errors to be re-thrown back into the window, you need to finalize the chain with Bluebird's .done() promise method, accessed in request-promise via the somewhat arcane .promise().done(). But even in that case you'd still need to specify that you're doing an async test.

In short, it's not entirely clear what you think some of this code is supposed to be doing, and how it differs from your expectations. Please clarify!

var rp = require('request-promise');

rp.get({ // start an async call and return a promise
    uri: 'http://httpstat.us/500',
    transform: function(body, res){
        res.data = JSON.parse(body);
        return res;
    }
}).then(function(res){ // if rp.get resolves, push res.data
    results.push(res.data);
})
.catch(function(err){ // if rp.get rejects (e.g. 500), do this:
    should.throw.error.to.console(); // test if something is thrown (but nothing has been!)
    var respErr  = JSON.parse(err.error);
    var errorResult = {
        origUrl: respErr.origUrl,
        error: respErr
    };
    results.push(errorResult); // push an object with some of the error info into results
});

// this line (e.g., end of script) is reached before any of the async stuff above settles. If you are testing something, you need to make the test async and specify when it's complete by invoking `done()` (not the same as ending the promise chain in Bluebird's `.done()`).
like image 61
Gabriel L. Avatar answered Oct 26 '22 17:10

Gabriel L.