3

In my project, I have a scenario where I have different kind of functions with different arguments. One of those arguments holds the callback function. And also there is a case where the function doesn't have any arguments. As shown below:

abc(string, function, number)
aaa(function, string)
bcd()
xyz(function)
cda(string, number, function, string)

I need to write a function such that irrespective of the irregularity of above functions, the function should return a promise.

Example:

// Assume $q has been injected
var defer = $q.defer();
function returnPromise(functionName, functionArgumentsInArray){
    defer.resolve(window[functionName].apply(null, functionArgumentsInArray));
    return defer.promise;
}

As you can see, the above function doesn't resolve the callback function but just the function.

Note: The functions will have max of one function argument or none.

I know it can be done individually for each case as follows:

function returnPromise(){
    var defer = $q.defer();
    abc("hey", function() {
        defer.resolve(arguments);
    }, 123);
    return defer.promise;
}

But I am looking for a common wrapper for all such functions.

Mr_Green
  • 40,727
  • 45
  • 159
  • 271
  • If it is not possible to know which (or whether any) of the parameters takes the callback, it also is impossible to write a generic wrapper. – Bergi Mar 13 '17 at 10:08
  • @Bergi I have a slight idea to do something with arguments and typeof to check for function type. but don't know what to do next. – Mr_Green Mar 13 '17 at 10:11
  • There are no arguments yet when you're just wrapping the function, and when the wrapper is called the callback will be omitted - and you have no chance to know in which position it belongs if any. – Bergi Mar 13 '17 at 10:17
  • Btw, the function that doesn't take an argument (an no callback therefore), why would you want to wrap that in a promise-returning function at all? – Bergi Mar 13 '17 at 10:19
  • @Bergi I am doing something like `returnPromise().then` later.. – Mr_Green Mar 13 '17 at 10:23
  • @Bergi `There are no arguments yet when you're just wrapping the function` - I am passing the arguments in the wrapper function. Please check first example. I think I need to replace the callback function somehow maybe using splice. – Mr_Green Mar 13 '17 at 10:25
  • But when the wrapped function is returning a promise, you would not want to pass in a callback that one could replace? – Bergi Mar 13 '17 at 10:35
  • @Bergi Yeah I think that is fine. or I will just pass something like null to then replace it with a function. because in that function I am just resolving the arguments. – Mr_Green Mar 13 '17 at 10:40

1 Answers1

3

I think you are looking for something like

const cbPlaceholder = Symbol("placeholder for node callback");
function wrap(fn) {
    return function(...args) {
        return $q((resolve, reject) => {
            function nodeback(err, result) {
                if (err) reject(err);
                else resolve(result);
            }
            for (var i=0; i<args.length; i++)
                if (args[i] === cbPlaceholder) {
                    args[i] = nodeback;
                    break;
                }
            const res = fn.apply(this, args);
            if (i == args.length) // not found
                resolve(res);
        });
    };
}

You could use it as

const abcPromised = wrap(abc);
abcPromised("hey", cbPlaceholder, 123).then(…)

Also have a look at How do I convert an existing callback API to promises?. In general you should not need that placeholder thing when all promisified functions follow the convention to take the callback as their last parameter.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375