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.
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 see 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.
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.