5

I am learning the ES6 standard so I start from a very basic example code.

There are callback hells exist in JavaScript so this time I do want to avoid using callbacks. But I met a problem that I don't really know how to convert a callback style code to a promise.

For example, if I have such code looks like below

module.exports = (x, y, callback) => {
  try {
    if (x < 0 || y < 0) {
      throw new Error('Rectangle dimensions are wrong.');
    } else {
      callback(null, {
        perimeter() {
          return (2 * (x + y));
        },
        area() {
          return (x * y);
        },
      });
    }
  } catch (error) {
    callback(error, null);
  }
};

How should I convert it to a Promise in ES6? Is that a kind of recommended behavior that convert callbacks to promises?

I have read this example but I was actually confused by the result. I think before I start to rewrite my callbacks to promises I need to understand this first.

let promise = new Promise(function(resolve, reject) {
  console.log('Promise');
  resolve();
});

promise.then(function() {
  console.log('Resolved.');
});

console.log('Hi!');

// Promise
// Hi!
// Resolved 

My understanding is that Promise runs immediately after getting created. But I don't know why the code in then method will be run last.

Kulbear
  • 871
  • 2
  • 10
  • 24
  • 1
    if you read the specifications of Promise/A+ [here](https://promisesaplus.com/) pay particular attention to 2.2.4 and the associated Note 3.1 - basically, the `.then` callbacks are asynchronous – Jaromanda X Aug 14 '16 at 04:38

4 Answers4

2

The existing answers fall prey to the deferred anti-pattern. I would avoid this approach as it is unnecessarily verbose and doesn't take advantage of the full Promise API. Another of the answers uses promisification. It's really only necessary to use promisification when you are not able to change the code that is written using the callback-style, for example with third-party scripts.

You are asking two questions, the second of which is why do Promises behave the way that you have seen in your given example. To find answers to this question I suggest you use the many existing questions on SO of this nature. For example, aren't Promises just callbacks?

As for your first question, of how to refactor your code to use Promises, here is my suggestion:

module.exports = (x, y) => {
  if (x < 0 || y < 0) {
    return Promise.reject(new Error('Rectangle dimensions are wrong.'));
  } else {
    return Promise.resolve({
      perimeter() {
        return (2 * (x + y));
      },
      area() {
        return (x * y);
      },
    });
  }
};

// e.g. success
createRectangle(10, 10)
  .then(rect => {
    console.log(rect.area()) //=> 100
  })

// e.g. failure
createRectangle(-1, -1)
  .catch(err => {
    console.log(err) //=> "Error: Rectangle dimensions are wrong."
  })

As the function doesn't itself depend on the completion of an asynchronous operation we can use the helper methods Promise#resolve and Promise#reject to return a Promise from the function that represents the success or failure of creating the "rectangle" object. These produce a new Promise whose status is resolved or rejected with either a value or an error, respectively.

sdgluck
  • 24,894
  • 8
  • 75
  • 90
0
module.exports = (x, y, callback) => {
  new Promise(function(resolve, reject) {
    if (x < 0 || y < 0) reject(new Error('Rectangle dimensions are wrong.'))
    else resolve({
      perimeter() {
        return (2 * (x + y));
      },
      area() {
        return (x * y);
      }
    })
  })
  .then(callback)
  .catch(callback)
}

Remember ".then" and ".catch" is asynchronous.

xblymmx
  • 39
  • 2
  • 3
0

This is an important topic. For the sake of functional coding in a synchronous looking manner the point of using promises is to move the logic in the callback to the then stage. So although you could you shouldn't handle the logic within the promise itself but at the then stage. This way of thinking helps us to create a general purpose promisify utility function which works for all certain type callback structures. In your case the callback type is the Node standard error first type. So as per your code below

module.exports = (x, y, callback) => {
  try {
    if (x < 0 || y < 0) {
      throw new Error('Rectangle dimensions are wrong.');
    } else {
      callback(null, {
        perimeter() {
          return (2 * (x + y));
        },
        area() {
          return (x * y);
        },
      });
    }
  } catch (error) {
    callback(error, null);
  }
};

a general promisfy function should works as follows;

var moduleExports = (x, y, callback) => {
  try {
    if (x < 0 || y < 0) {
      throw new Error('Rectangle dimensions are wrong.');
    } else {
      callback(null, {
        perimeter() {
          return (2 * (x + y));
        },
        area() {
          return (x * y);
        },
      });
    }
  } catch (error) {
    callback(error, null);
  }
};

function promisfy(fun, ...args){
  return new Promise((v,x) => fun(...args, (err,data) => !!err ? x(err) : v(data)));
}

var p = promisfy(moduleExports,4,5);
p.then(val => console.log(val,val.area(),val.perimeter()), err => console.log(err));

// p.then(val => callback(null,val), err => callback(err))

So in this particular case you have your resulting object with the area and perimeter functions supplied to the then stage onFulfillment as a value argument. Accordingly you should invoke the logic to utilize them in the onFulfillment function (then stage first callback) and handle any errors in the onReject function (then stage second callback). I show this in the last line as a comment in the above snippet.

PS: I use v (looks like check) to designate resolve and x (obvious) to designate the reject callbacks of promises.

Redu
  • 25,060
  • 6
  • 56
  • 76
-1

Promises are really good but at the beginning could be a little bit confused, check this code:

module.exports = (x, y) => {
var deferred = q.defer();// this will be our promise
  try {
    if (x < 0 || y < 0) {
      //return an error to be catched by catch method
      deferred.reject(new Error('Rectangle dimensions are wrong.'));
    } else {
      //return the event to the next function with this value
      deferred.resolve({
        perimeter() {
          return (2 * (x + y));
        },
        area() {
          return (x * y);
        },
      });
    }
  } catch (error) {
    deferred.reject(error);
  }

 return deferred.promise; //Here return the promise
};

//You will use it like this
module(x,y)
.then(callback)
.then(function(data){
 })
.catch(function(error){
});

In my example when you call your module, you will get the promise right away, but the code is not yet executed, after the code is executed, you will get the event in your "then" method or if something happens in your catch.

I really like q library to handle promises, give you a lot of control on how to return errors and stop the chain if something wrong happens. Basically, you has more control on your flow of functions.

Hope it help you

damianfabian
  • 1,681
  • 12
  • 16