Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending `Promise` and change `then` signature

I want to extend Promise and change the then signature so its callback receives two values. I tried different approaches two of which are documented and tested here. Sadly, I get various errors or the resulting class does not behave like a Promise.

Approach 1: Wrapping a native Promise

export class MyWrappedPromise {
  constructor(data) {
    this.data = data;
    this.promise = new Promise(evaluate.bind(data));
  }

  then(callback) {
    this.promise.then(() => callback(this.data, ADDITIONAL_DATA));
  }

  catch(callback) {
    this.promise.catch(callback);
  }
}

Approach 2: Extending native Promises

export class MyExtendedPromise extends Promise {
    
  constructor(executor, data) {
    super(executor);
    this.data = data;
  }

  static create(data) {
      return new MyExtendedPromise(evaluate.bind(data), data);
  }

  then(callback) {
    return super.then(() => callback(this.data, ADDITIONAL_DATA));
  }
}

Does anyone have any suggestion on what I am doing wrong? Feel free to create a PR on GitHub.

thanks

------------------- Edit ---------------------

Some Additional code and info to make the code above more understandable without looking at the code and tests on Github.

evaluate is just the Promise executor function. I extracted it out so I can keep it consistent across all my implementations and tests. It may look convoluted but it's structured that way to simulate my "real" project.

export function evaluate(resolve, reject) {
  const data = this;
  function getPromise(data) {
    return !!data ? Promise.resolve(data) : Promise.reject(new Error("Error"));
  }

  getPromise(data)
    .then(resolve)
    .catch(reject);
}

ADDITIONAL_DATA is just a string to simulate the second value in the callback. It's also extracted to be consistent across all versions and tests.

------------------- Edit 2---------------------

Errors that come up depending on the solution

  • catch is not accessible
  • A lot of UnhandledPromiseRejectionWarning: warnings because errors/rejects are not getting propagated up correctly.
  • Errors/rejects are getting thrown too early and don't even reach the rejects checks in my test suites
like image 309
KenavR Avatar asked Dec 21 '18 10:12

KenavR


2 Answers

You have problems (especially with unhandled rejections) because you are not implementing the then interface correctly. Remember that .catch(onRejected) is just an alias for .then(undefined, onRejected), and then with two parameters is the actual core method of every promise.

You were always ignoring the second argument, so no rejection ever got handled. You need to write

then(onFulfilled, onRejected) {
  return super.then(res => onFulfilled(res, this.ADDITIONAL_DATA), onRejected);
  // or `this.promise.then` instead of `super.then`
}
like image 70
Bergi Avatar answered Nov 15 '22 04:11

Bergi


I don't understand very well why you do have a factory method, instead of using directly the constructor.

Do you mean something like this?

class MyExtendedPromise extends Promise {

  constructor(executor, data) {
    super(executor);
    this.data = data;
  }

  then(callback, test) {
    console.log('passed new parameter in then:', test);
    console.log('additional data:', this.data);
    return super.then(data => callback(data, test));
  }
}

new MyExtendedPromise((resolve, reject) => {
	setTimeout(() => resolve(true), 2000);
}, 'other additional data').then(data => console.log('my then', data), 'hello world');
like image 25
quirimmo Avatar answered Nov 15 '22 04:11

quirimmo