3

I am not able to catch the Exception/Error that is happening inside of the Resolve promise. Can someone explain if there is a way to catch this error:

createAsync = function() {
    var resolve, reject,
    promise = new Promise(function(res, rej) {
        resolve = res;
        reject = rej;
    });
    promise.resolve = resolve;
    promise.reject = reject;
    return promise;
};

promise = createAsync()
promise.then(function myresolver(){
    throw Error('wow')
})


// Catching the error here
try {
  promise.resolve()
} catch(err) {
  console.log( 'GOTCHA!!', err );
}

EDIT:

Let me explain me better, I'm trying to create an API and the user only have access to the promise resolve/reject part:

// This is my API user does not have access to this part
promise = createAsync()
promise
.then(function(fun) {
    if (typeof fun != 'function')
        throw Error('You must resolve a function as argument')
}).catch(function(err){
    // Some internal api tasks...
    return Promise.reject(err)
})

Now the solution I would like to give him, but does not work:

// Here is where the user can resolve the promise.
// But must be able to catch the error
promise.catch(function(err){
    console.log("GOTCHA", err)
})
promise.resolve('This must be a function but is a string');

Any ideas?


More info: Yes is an antipattern for the common usage. This is a remote procedure call API, so the user must be able to reject or resolve. And the calls are async so the best approach is to use promises. I wouldn't say is an antipattern for this case. Actually is the only pattern. (I can't use Async/Await)

Enzo
  • 4,111
  • 4
  • 21
  • 33
  • `// Catching the error here` no you aren't - you `promise.resolve()` doesn't reject the promise ... your `promise.then ... throw` will "reject" THAT promise (the one returned by .then) not the one in `promise` – Jaromanda X Dec 21 '16 at 03:38
  • 3
    "Exporting" the `resolve` and `reject` functions provided to the executor (the function passed to the promise constructor), in your case by polluting the promise object with additional properties, is an anti-pattern. The entire point of the design of the promise constructor is that resolving and rejecting is isolated within the executor. Your approach allows any random person who happens to acquire one of your promise objects to resolve or reject it, which is almost certainly not good program design. –  Dec 21 '16 at 04:16
  • Can I add that to my answer - I was trying to think of how to say that, but never came up with anything that described it that eloquently – Jaromanda X Dec 21 '16 at 04:19
  • @torazaburo Yes is an antipattern for the common usage. This is a remote procedure call API, so the user must be able to reject or resolve. And the calls are async so the best approach is to use promises. I wouldn't say is an antipattern for this case. Actually is the only pattern. (I can't use Async/Await) – Enzo Dec 21 '16 at 04:25
  • 1
    @EnZo torazaburo is right, you should always separate resolving capabilities from the promise. And usually you don't want a user to call `resolve` or `reject` - the better promise pattern is to simply accept a promise from the user that he can resolve/reject at his own disposal. – Bergi Dec 21 '16 at 04:38
  • @Bergi "And usually you don't want a user to call resolve or reject" Again, is a remote procedure call API, so user must be able to resolve or reject. – Enzo Dec 21 '16 at 04:43
  • 1
    On which side of the RPC is the "user" sitting? It might help if you could show the actual code (some API object?) instead of the `createAsync` pseudocode. – Bergi Dec 21 '16 at 04:57
  • Both, client and server. https://github.com/DistributedObjectProtocol/dop/blob/master/dist/browser.js#L2013 – Enzo Dec 21 '16 at 05:07
  • 1
    Oh, you were right, the real code *is* complicated. Unfortunately I couldn't figure out what the `f` in that `localProcedureCall` was and how it uses/needs to use the `req` object. However, I can tell you that `localProcedureCall` should not take `resolve` and `reject` callbacks but rather *return* a promise (and you likely [want to use `.then(…, …)` instead of `.then(…).catch(…)`](http://stackoverflow.com/q/24662289/1048572)), and that the pattern for `dop.protocol.subscribe` basically should look like `return new Promise(resolve => storeRequest(node, request_info, resolve); })` – Bergi Dec 21 '16 at 05:30
  • `f` is the function of the user defined with the API. That function has `req` as last parameter and the user can `req.reject` or `req.resolve` accordingly of his needs. Yes, `localProcedureCall` should take `resolve` and `reject`. Because is the way that I can reuse the function for other localProcedureCall tasks. – Enzo Dec 21 '16 at 06:36

3 Answers3

3

Let me explain me better, I'm trying to create an API and the user only have access to the promise resolve/reject part. Now the solution I would like to give him does not work. Any ideas?

Don't put everything onto that initial promise object. It sounds what you really need to expose to your user is a) a way to resolve (which includes rejection) and b) a way to get the result (promise) back. So all you need to do is give him a function:

function createAsync() {
    var resolve;
    var promise = new Promise(function(r) { resolve = r; })
    .then(function(fun) {
        if (typeof fun != 'function')
            throw Error('You must resolve a function as argument')
    }).catch(function(err){
       // Some internal api tasks...
       throw err;
    });
    return function(value) {
        resolve(value);
        return promise;
    };
}

and he'd use it like

var start = createAsync();
// possibly later
start('This must be a function but is a string').catch(function(err){
    console.log("GOTCHA", err)
});
// or
start(Promise.reject(new Error('doomed to fail'))).catch(…);

but actually this pattern is still convoluted and much too complicated. Simply give your createAsync function a parameter (that might receive a promise if the user needs to delay passing the fun) and be done with it:

function createAsync(valu) {
    return Promise.resolve(val).then(function(fun) {
        if (typeof fun != 'function')
            throw Error('You must resolve a function as argument')
    }).catch(function(err){
       // Some internal api tasks...
       throw err;
    });
}

createAsync('This must be a function but is a string').catch(function(err) {
    console.log("GOTCHA", err)
});
// or
createAsync(new Promise(function(resolve) {
    // later
    resolve('This must be a function but is a string');
})).catch(function(err) {
    console.log("GOTCHA", err)
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • `createAsync` instance must be created internally. I can't allow to the user create the promise because I need the instance to resolve internally. – Enzo Dec 21 '16 at 04:50
  • It seems you need to wait for the user to call `resolve` anyway, so there shouldn't be any difference? But if you absolutely have to, just use the pattern from the first two snippets. – Bergi Dec 21 '16 at 04:54
  • It might help if you could share/link your real code. – Bergi Dec 21 '16 at 04:55
  • First, two snippets do not allow user to reject. Real code is very complicated. The edited example is the most simple and accurate way to understand. – Enzo Dec 21 '16 at 05:04
  • They do, see the last line of the second snippet. – Bergi Dec 21 '16 at 05:08
2

Warning: as pointed out by @torazaburo "Exporting" the resolve and reject functions provided to the executor (the function passed to the promise constructor), in your case by polluting the promise object with additional properties, is an anti-pattern. The entire point of the design of the promise constructor is that resolving and rejecting is isolated within the executor. Your approach allows any random person who happens to acquire one of your promise objects to resolve or reject it, which is almost certainly not good program design.

With that warning in mind, your call to promise.resolve() doesn't reject the promise ... your promise.then ... throw will "reject" THAT promise (the one returned by .then) not the one in promise

rewrite your code as follows:

promise = createAsync()
promise // the call to promise.resolve will resolve THIS promise, so .then will execute
.then(function myresolver(){
    throw Error('wow')
}) // .then returns a NEW promise, in this case rejected with Error('wow')
.catch(function(e) {
    console.log("GOTCHA", e)
});

promise.resolve()

and you'll see the expected GOTCHA in the console

Jaromanda X
  • 53,868
  • 5
  • 73
  • 87
  • Another thing worth pointing out is that he could use `try/catch` with ES7's `async` functions – nem035 Dec 21 '16 at 03:43
  • 1
    ES7's async functions are a step back with respect to asynchronous programming :p - plus, that's a huge change to the code, rather than a relatively minor one in the answer – Jaromanda X Dec 21 '16 at 03:44
  • 1
    Sure but that's just your subjective opinion and not the point :). An objective fact is that you can use `try/catch` with them. And yes the code will need to completely change so that's why I said "worth pointing out" :). I guess "worth mentioning" would've worked more accurately. – nem035 Dec 21 '16 at 03:45
  • 1
    here's the thing, I wouldn't even know how to rewrite that code to use async/await + try/catch. While I understand (and have used) async/await I'm still a babe in the woods with it :p you're welcome to educate me :p – Jaromanda X Dec 21 '16 at 03:50
  • @nem035 Have you considered posting your own Answer demonstrating the suggested approach, including explanation of why OP is getting the results described at Question? – guest271314 Dec 21 '16 at 03:55
  • 1
    @guest271314 all I meant was a friendly suggestion fellas :P. As requested, I've posted my own example for this – nem035 Dec 21 '16 at 04:15
  • 2
    @nem035 - didn't think the suggestion was unfriendly at all - thanks for the lesson by the way – Jaromanda X Dec 21 '16 at 04:17
  • @JaromandaX always happy to discuss code with fellow hackers :P – nem035 Dec 21 '16 at 04:24
1

Jaromanda's answer already explained that you must use a rejection handler or catch to handle asynchronous errors using promises.

Also make sure to read torazaburo's comment explaining why your approach is an anti-pattern.

If you really must use this approach and also require try/catch for some reason, ES7 gives you a way via async functions

(async function f() {
  const createAsync = function() {
    var resolve, reject,
      promise = new Promise(function(res, rej) {
        resolve = res;
        reject = rej;
      });
    promise.resolve = resolve;
    promise.reject = reject;
    return promise;
  };

  const promise = createAsync();
  promise.resolve();

  // Catching the error here
  try {
    await promise.then(function() {
      throw Error('wow')
    });
  } catch (err) {
    console.log('GOTCHA!!', err);
  }
})()
nem035
  • 34,790
  • 6
  • 87
  • 99
  • Sorry, I can't use async/await. – Enzo Dec 21 '16 at 04:15
  • 2
    oh, that's funny - sorry @nem035 - but you taught me a thing so not in vein – Jaromanda X Dec 21 '16 at 04:16
  • 1
    I don't see any reason to put `createAsync` inside that IIAFE. And you don't need to name `f`. – Bergi Dec 21 '16 at 04:32
  • 1
    You're absolutely right but not sure what's your overall point since those are minor details that don't change much. My example was just to demonstrate `try/catch` with promises, in terms of his code, other people have already suggested better approaches. – nem035 Dec 21 '16 at 04:42