14

I often see in other peoples docs something like:

Callback is optional, if omitted returns a promise.

This is what I have:

export function doSomeAsync(options, callback) {

    const useCallback = (callback && typeof callback == 'function');

    const promise = new Promise((resolve, reject) => {

        // --- do async stuff here ---
        const check = (options.num === 1) ? true : false;
        setTimeout(() => {
            if (check) {
                finish(true, "Number is 1");
            } else {
                finish(false, new Error("Number is not 1"));
            }
        }, 1000);
        // ---------------------------

        function finish(ok, rtn) {
            if (useCallback) {
                if (ok) {
                    callback(null, rtn);
                } else {
                    callback(rtn, null);
                }
            } else {
                if (ok) {
                    resolve(rtn);
                } else {
                    reject(rtn);
                }
            }
        }

    });

    return (useCallback) ? false : promise;
}

The finish() function just avoids lots of if... statements scattered around.

I'm creating a promise object, whether or not I use it.

Testing like this:

doSomeAsync({ num: 1 }).then((result) => {
    console.log('p result', result);
}).catch((err) => {
    console.log('p err', err);
});

doSomeAsync({ num: 1 }, (err, result) => {
    if (err) {
        console.log('cb err', err);
    } else {
        console.log('cb result', result);
    }
});

This works, but I'm wondering if this is the best way, or if others have a better and more succinct implementation..?

3
  • Might be more appropriate for codereview.stackexchange.com Commented Apr 25, 2016 at 10:34
  • 1
    What promise library are you using? Bluebird even has a dedicated function for this. Commented Apr 25, 2016 at 12:48
  • I'm running Node v4.2.2, with Babel for ES6 (babel-preset-es2015 6.6.0), but I haven't installed the specific babel ployfill for promises. So native Node promises I guess. Commented Apr 25, 2016 at 13:19

2 Answers 2

24

This could be simplified if you simply always used the promise, which you're always creating anyway:

export function doSomeAsync(options, callback) {
    const promise = new Promise((resolve, reject) => {
        const check = (options.num === 1) ? true : false;
        setTimeout(() => {
            if (check) {
                resolve("Number is 1");
            } else {
                reject(new Error("Number is not 1"));
            }
        }, 1000);
    });

    if (callback && typeof callback == 'function') {
        promise.then(callback.bind(null, null), callback);
    }

    return promise;
}

Your function is always promise-based, also in the fact that it always returns a promise. The caller is simply free to ignore that. The callback argument is merely a "legacy fallback interface" (or "alternative interface" if you prefer) to using that promise.

Sign up to request clarification or add additional context in comments.

4 Comments

Yes, this is exactly the approach I was thinking. Returning false seems a bit of a waste - also (being a .NET dev as well as JS) seems odd returning two different types from such a function.
Thanks deceze... I've updated my question with test calls. After updating to use your code I get: p result Number is 1 and cb err Number is 1. Does the callback now only have one param?
@Stephen In my example above, the way the promise.then callbacks are bound, callback will receive one argument in case of success and two arguments (false and error) in case of failure. The callback is expected to have a signature of function (result, err). Design that interface whichever way you like.
I notice the answer was updated with promise.then(callback.bind(null, null), callback) to produce the Node style callback(err, result) I was looking for. Thank you Bergi & deceze!
1

You could get rid of all edge cases by always returning a promise, and define a default callback (a callback-shaped identity function) that handles the no-callback-supplied case:

const genericAsync = (stuff, callback = (e, i) => e || i) => new Promise(
  (resolve, reject) => doStuffWith(stuff, resolve, reject)
)
  .then(response => callback(null, response))
  .catch(callback);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.