ysmood / yaku

A lightweight promise library
https://tonicdev.com/ysmood/yaku
MIT License
290 stars 28 forks source link

Pass data (callback) to unhandledRejection handler #28

Closed svsool closed 8 years ago

svsool commented 8 years ago

I have a case, when I need pass callback to unhandledRejection handler and can't use catch in default maner, when I try define property for the promise, I notice that property do not passed in promise at onUnhandledRejection, how I can implement this behaviour?

Example:

const onUnhandledRejection = (reason, promise) => {
  // promise.handleRejection always undefined
  if (promise && typeof promise.handleRejection === 'function') {
    promise.handleRejection(reason);
  } else {
    console.error(reason);
  }
};

window.onunhandledrejection = ({ reason, promise }) => {
    onUnhandledRejection(reason, promise);
};

const handleRejection = (promise) => {
  Object.defineProperty(promise, 'handleRejection', {
    enumerable: false,
    configurable: false,
    writable: false,
    value: (reason) => {
      if (reason && reason instanceof SkipError) return;
      console.error('MIDDLEWARE ERROR:', reason, reason.stack);
      // ...customize global rejection handler for this promise
    }
  });

  return promise;
};

handleRejection(promise(client)).then(
  (result) => {
    // ...
  },
  (error) => {
    throw SkipError;
  }
);
ysmood commented 8 years ago

Define properties on a promise instance directly is a bad pattern. You have to capsule the callback function into the reason, not the promise itself.

ysmood commented 8 years ago

You don't have to use onunhandledrejection at all:

var SkipError = function () {};

var handleRejection = function (promise) {
  return promise.catch(function (reason) {
      if (reason && reason instanceof SkipError) {
        return;
      } else {
        console.error('MIDDLEWARE ERROR:', reason);
        return Promise.reject(reason);
      }
  });
};

var promise = new Promise(function (resolve, reject) {
  reject(new SkipError());
})

handleRejection(promise).then(function (result) {
    console.log('ok')
});
svsool commented 8 years ago

Thanks for the answer, but .catch not suitable for my app. I define properties on a promise directly, because I didn't find another way to pass callback to unhandleRejection in your lib, that customize global onhandlerejection for my superagent promise that used to fetch data throughout the app. In my case, .catch branch prevent calling rejection callback passed to .next in other place of app and I don't want wrap each .then with custom method that catch error if rejection method not passed, unfortunately promise don't have finallyCatch method, that don't prevent calling rejection passed to .then, so I use global handler for that, if rejection handler not passed, I skip it in global onhandlerejection with custom behaviour (through callback in promise), if rejection callback passed, then handle it in .then branch, sorry but it hard to explain. Maybe I can wrap Promise instance in my custom class? But anyway it wouldn't pass to onunhandledrejection directly, because somewhere in the lib it's replaced with another promise.

ysmood commented 8 years ago

Then you may want to use guard: https://github.com/ysmood/yaku#guardtype-onrejected

var Promise = require('yaku');
require('yaku/lib/guard');

class AnError extends Error {
}

Promise.reject(new AnError('hey'))
.guard(AnError, (err) => {
     // only log AnError type
     console.log(err);
})
.then(() => {
     console.log('done');
})
.guard(Error, (err) => {
     // log all error type
     console.log(err)
});