19

When working with asynchronious javascript every second function looks like this:

function_name()
{
    return new Promise((resolve,reject) => {
       // do some async task
       resolve();
    });
}

Even with es6 async/await I cannot avoid the part "return new Promise((resolve,reject) => { ...".

Is there another way of writing such code without all those duplicated lines?

Thanks.

David Vielhuber
  • 3,253
  • 3
  • 29
  • 34
  • you can use rxjx http://reactivex.io/rxjs/ observable with typescript – sumeet kumar Feb 01 '18 at 20:05
  • @Points: How should you write the following simple function test() { return new Promise((resolve,reject) => { setTimeout(() => { resolve(42); }); }); } with async/await? Thats not possible. – David Vielhuber Feb 01 '18 at 20:11
  • It depends a lot on what "do some async task" is - please post your real code. Are you employing the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it) everywhere? – Bergi Feb 01 '18 at 20:12
  • @DavidVielhuber No, it's indeed not possible to write that `test` example differently, but you would need this function that makes a promise for a timeout only *once* in your whole application. Not in every second method. – Bergi Feb 01 '18 at 20:14
  • @DavidVielhuber well I decided that that comment wasn't really accurate, since ultimately *something* will have to explicitly instantiate a Promise. – Pointy Feb 01 '18 at 20:14
  • @Bergi: Thanks in advance for that "Promise constructor antipattern". It indeed was the case that inside those functions there were other promises and I just constructed another promise because I needed to modify the result and then resolve it. – David Vielhuber Feb 01 '18 at 20:17
  • @Bergi: Can I also return a Promise immediately with the then() and catch() functions attached? Example: function test() { return someAsyncTaskThatReturnsAPromise().then((result) => { resolve(result.field); }).catch((error) => { reject(error); }); } – David Vielhuber Feb 01 '18 at 20:40
  • @Bergi: Yes, I can with function test() { return someAsyncTaskThatReturnsAPromise().then((result) => { return result.field; }).catch((error) => { return null; }); } – David Vielhuber Feb 01 '18 at 20:45
  • @DavidVielhuber Like jFriend wrote below, you usually shouldn't ignore errors, so don't use `.catch(() => null)`. – Bergi Feb 02 '18 at 00:17

3 Answers3

8

First off, you should be avoiding the promise anti-pattern that wraps a new promise around other functions that already return promises. If you're doing that, then you can just stop doing that entirely and just return the promise that is already being created by your async operation. That will immediately simplify things.

If you have older style callback-based operations that are not promisified, then in general, you should promisify the base async function just once and then code only with the version of the function that returns a promise. Then, all your logic flow is using promises and you won't see the new Promise(...) boilerplate in the regular flow of your code.

Further, you can generally avoid manually promisifying these days. If you're using node.js, then util.promisify() can be used to create a promisified version of any async function following the node.js calling convention.

Or, if you use the Bluebird promise library, you can either promisify an individual function with Promise.promisify() or you can promisify an entire interface with Promise.promisifyAll().

Using any of these alternate methods avoid the boilerplate you seem to be objecting to.

But, if you're going to manually promisify each function from scratch yourself and not use any of these other helper functions, then there is no way to avoid the new Promise(...) syntax. That's what it takes.


So, most of your coding should NOT involve new Promise(...). If it does, then you need to show us some of your examples and we can advise, better/different ways to code.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Is it good practice when I chain then and catch functions when returning existing promises? function test() { return someAsyncTaskThatReturnsAPromise().then((result) => { return result.field; }).catch((error) => { return null; }); } – David Vielhuber Feb 01 '18 at 22:12
  • 1
    @DavidVielhuber - Chaining `.then()` is perfectly normal for a lot of reasons. You have to be careful you're doing the right thing if you chain a `.catch()`. If you want to "eat" the error and return a value instead, even when there's an error, you can certainly do that, but that usually not the desired design because it tells the calling code there was no error when there actually was. There are, of course, some situations where you want to "handle" the error and return a non-error condition, but that is less common that returning an error when there's an error. – jfriend00 Feb 01 '18 at 22:16
  • @DavidVielhuber - Obviously, it all depends upon your design, but typically you want errors to propagate (return as a rejected promise) so the caller knows when there was and wasn't an error. If the caller is, themselves, chaining, it's much easier for them if an error is a rejected promise because their chain will get automatic error handling. If you `.catch()` and `return null` instead, then the caller now has to check for `null` to decide whether their chain should continue or not. If you let it reject, then the callers error handling is more automatic. – jfriend00 Feb 01 '18 at 22:17
3

Was reading through the comments and wanted to clarify:

How should you write the following simple function test() { return new Promise((resolve,reject) => { setTimeout(() => { resolve(42); }); }); }

That looks like a promise resolving to 42 immediately so you can:

const test = _=>Promise.resole(42);

If you want a promise that resolves at a certain time and use it multiple times you can write the following:

const later = time => value =>
  new Promise(
    (resolve,reject)=>
      _=>resolve(value),
      time
  );
const afterOneSecond = later(1000);
afterOneSecond("resolves after one second");

If you would like to reject something later:

later(1000)(Promise.reject("rejects after one second"));

If you're testing with real promises and not mocked ones and need to pass around a rejected promise without the console warning you and hitting "uncaught" breakpoints you can do:

const p = Promise.reject("rejects after one second");
p.catch(ignore=>ignore);//catch it
later(1000)(p);//pass promise without catch to later

If you have a function that returns a promise of something and process that value you can do.

myFunction()
.then(
  something=>something.data
)
.then(
  data=>...
)

If you want to check if something.data is not empty and if it is you want to reject you can do:

myFunction()
.then(
  something=>
    (something&&something.data.length!==0)
      ? something.data
      : Promise.reject("Data cannot be empty")
)
.then(
  data=>...
)
.catch(
  e=>
    (e==="Data cannot be empty")
      ? "do something special"
      : Promse.reject(e)//keep rejecting, other error
);

If you have a synchronous function that can throw, it's the first function in your promise chain and you want what it throws to end up as rejected promise you can do the following:

const syncFunctionThatThrows = arg => {
  if(arg===1){
    throw "arg cannot be 1";
  }
  return arg;
};

//starting promise chain with synchronous function that can throw
//  if it throws the error is absorbed by the chain and produces
//  a rejected promise
Promise.resolve(1)//going to pass 1 to syncFunctionThatThrows
.then(syncFunctionThatThrows);

To use a callback api as promise you can do:

const asPromise = object => fn => args =>
  new Promise(
    (resolve,reject)=>
      fn.apply(
        object,
        args.concat([
          (...result)=>
            (result[0])//first argument of callback is error
              ? reject(result[0])//reject with error
              : resolve(result.slice(1,result.length))//resolve with result(s)
        ])
      )
  );

const callbackApiObjectAsPromise = asPromis(callbackApi);
callbackApiObjectAsPromise(callbackApi.someMethod)(["arg1","arg2"])
.then(
  result=>...
)
//example of mysqljs
const connectionAsPromise =  asPromise(connection);
connectionAsPromise(connection.query)([
  'SELECT * FROM `books` WHERE `author` = ?',
  ['David']
]).then(
  ([results, fields])=>...
);

If you're not into diy you can use promisifyAll or promisify as Pointy suggested.

HMR
  • 37,593
  • 24
  • 91
  • 160
0

This might be old, but I will put my answer as it might help some lost souls out there. First you need to define global functions (I'm using globals. for nodejs, can be set to any global object like window. for browser)

global.res= function (v){
    return new Promise(function(r){r(v);});
}
global.rej= function (v){
    return new Promise(function(r,rr){rr(v);});
}

Then you can use res/rej in your code like this:

async function testing(param){
    return param < 10 ? res(true) : rej(false)
}

console.log(await testing(2)) // shows 'true'
console.log(await testing(20)) // throws error with 'false' as value
Xakiru
  • 2,556
  • 1
  • 15
  • 11