0

I have promisified the fbgraph nodeJS API and am using it to test a feature. This feature posts to a given Facebook Group, then verifies the post has gone through correctly and deletes the post. The purpose of this code is to test that the user has post permissions for a different array of groups.

The code as it's currently implemented using Bluebird's Promise library, and generators.

var examplePostId = 12345678910

facebookPoster = Promise.coroutine(function* (feedId) {

  var postResponse = yield graph.postAsync(feedId + '/feed', sampleData);
  var postId = postResponse.id
  var getResponse = yield graph.getAsync(postId)
  var returnedId = getResponse.id
  var postedSuccessfully = true // <-- This is what I want to reference in my error handler.
  var deleteResponse = yield graph.delAsync(postId)
  var getAfterDeleteResponse = yield graph.getAsync(postId) // I expect this to throw an error since the post should already be deleted
  return postId

})

facebookPoster(examplePostId).then(function(postId){
  console.log(postId);
}).catch(function(err){
  console.log(err); // How can I tell the handler that error is fine because postedSuccessfully is true?
})

Here's my snafu: FB's Graph API is horrifically unreliable. Even if the post is not actually deleted, I will still receive a success response (documented here: ___).

Therefore, in my generator I am attempting to GET the information for the postID a second time, and am expecting it to blow up. When I do receive an error, it gets passed to my handler and my handler is triggered. This is fine, but I would like to be able to reference the postedSuccessfully boolean value to differentiate between an error that I was expecting to receive, and an error that was unexpected.

How can I reference the postedSuccessfully boolean value, or implement another graceful way to differentiate between the received errors?

Anthony
  • 13,434
  • 14
  • 60
  • 80
  • I don't really get it. That boolean seems to be always `true`. What exactly do you want to catch where it would not be true? – Bergi Jun 21 '15 at 21:26
  • possible duplicate of [How do I access previous promise results in a .then() chain?](http://stackoverflow.com/q/28250680/1048572), but I'm going to try to give a specific answer – Bergi Jun 21 '15 at 21:28
  • If for example, the first getAsync call returns an error, it will pass it to the handler and at that moment, if I conditionally check in the handler for the value of postedSuccessfully, the handler can know that this error was received before the delAsync call was made. However, if the handler can check the value of postedSuccessfully and see that it is true, it knows the post went through successfully and can tell me that the permissions are there to make posts. Let me know if that makes sense. – Anthony Jun 21 '15 at 21:32
  • By the way, I apologize but I had deleted the second get call! I've added it into the code now, hopefully it makes more sense now, my bad! – Anthony Jun 21 '15 at 21:36
  • 1
    Ah yes, now I think my answer makes sense as well :-) – Bergi Jun 21 '15 at 21:44

2 Answers2

1

Lots of ways to do this. Probably the easiest way is to make your postId into an object that holds state:

var examplePost = {
    id: 14715395189,
    postedSuccessfully: false
};

...and then you'll be able to test it whenever you like. Another (more complex) way is to break into multiple promises, but I'm not sure I could have a stab at the code without understanding your existing functionality a bit better.

Duncan Thacker
  • 5,073
  • 1
  • 10
  • 20
  • Yeah I was going to do exactly that, but the problem is that the code in this example would blow up before I could return the value of postId, and the error handler function won't be able to check the values of the examplePost object. – Anthony Jun 21 '15 at 21:33
  • Can you do it for the passed-in object? That always exists, right? – Duncan Thacker Jun 21 '15 at 21:36
  • I will try that for sure. One thing I am trying is declaring postedSuccesfully as an empty string at the top of my code, and then trying to change its value to true on the inside of my code, but I can't change the variable's value in the outer scope. I swear I should be able to use the .apply method to manually change the inner scope's context to the outer scope's but I can't put my finger on exactly how in Node haha. But I will try your answer in the meantime, I think it may be possible, or I may run into the same error where I can't change the variable declared in the outer scope. – Anthony Jun 21 '15 at 21:49
1

Maybe I didn't really understand what you want to do, but one of the benefits of generator functions is that you can use try-catch-statements inside of them as you are used to.

So you could do what you described in your text by using

var facebookPoster = Promise.coroutine(function* (feedId) {
  try {
    var postId = (yield graph.postAsync(feedId + '/feed', sampleData)).id;
    var returnedId = (yield graph.getAsync(postId)).id;
    var postedSuccessfully = true;
    var deleteResponse = yield graph.delAsync(postId);
    var getAfterDeleteResponse = yield graph.getAsync(postId);
  } catch (e) {
    if (!postedSuccessfully) // ignore error, it's expected
      throw e;
  }
  return postId;
});

facebookPoster(12345678910).then(console.log.bind(console), console.error.bind(console));

However I'd consider the following to be cleaner:

var facebookPoster = Promise.coroutine(function* (feedId) {
  var postId = (yield graph.postAsync(feedId + '/feed', sampleData)).id;
  var returnedId = (yield graph.getAsync(postId)).id;
  var deleteResponse = yield graph.delAsync(postId);
  try { // sometimes deleteResponse is wrong, so double-check
    var getAfterDeleteResponse = yield graph.getAsync(postId);
  } catch (e) {
    // ignore error, it's expected
  } finally {
    if (getAfterDeleteResponse)
      throw new Error("hey it's still there while it shouldn't be!");
  }
  console.log(postId);
});

facebookPoster(12345678910).catch(console.error.bind(console));

Or even better, avoiding that horrible try-catch-finally thing by using the then(success, fail) pattern:

  …
  // sometimes deleteResponse is wrong, so double-check
  var getAfterDeleteResponse = yield graph.getAsync(postId).then(function(post) {
    throw new Error("hey it's still there while it shouldn't be!");
  }, function ignoreExpectedError(){});
  …
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thanks a lot for this really awesome code. :) Lots of different tools in the toolbox for me to play with and I had never tried a try-catch statement in a generator before! Thanks ad infinitum! – Anthony Jun 21 '15 at 21:55
  • @mrmayfield: Yes indeed :-) However (maybe you've seen me constantly editing the answer) there are many pitfalls. I wouldn't recommend to use `try-catch` for cases where you want to treat the success and exception cases separately - JS lacks some kind of `try - catch - else` :-) – Bergi Jun 21 '15 at 21:59
  • Yes, I have played with the try catch flow and have it working, but I will probably use the final promise's then(success,fail) pattern in the end. I also started using generators this morning thanks to your aforementioned 'How do I access previous promise results in a .then() chain?' answers so I've definitely come full-circle with your help today hahaha. :) – Anthony Jun 21 '15 at 22:03