javascript – How do I convert an existing callback API to promises?

Necromancing a little, bit this link may be useful….


TLDR; look at the snippet example at the end of this answer



  • promiseToCB converts and exports an existing function that’s been previously coded to return a promise
  • cbToPromise converts and exports an existing function that’s been previously coded to call the last argument with (error,result)
    • if wrapped function supplies more than 1 result, the result will be an array of results
    • eg cb(undefined,path,stat) —> resolve([path,stat]) / cb(undefined,[path,stat])
  • asPromise lets you code a new function to return a promise, but it can be called either way
  • asCallback lets you code a new function to call cb(err,result)but it can be invoked either way

sample functions

Each sample takes 2 arguments, and resolves/rejects/errors based on random number.

arg2 can also be used to force pass or fail. (looks for “-pass” or “-fail”).

wrap existing functions

  • exports the function to current “this” (or use promiseToCB(function myFunc(){},newThis); )


    promiseToCB(function sampleFunc1(arg1,arg2) {
        console.log("deciding:",arg1,arg2);
        return new Promise(function(resolve,reject){
       
           const timer = setTimeout(function(){reject([arg1,arg2,"ouch"].join("-"));},5000);
           
           setTimeout(function(){
               if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
    
                   console.log("complete:",arg1,arg2);
                   clearTimeout(timer);
                   resolve([arg1,arg2,"all good"].join("-"));
               }
           },2000);
        
        });
    });
    
    cbToPromise('sampleFunc2',function someOtherName(arg1,arg2,cb) {
       console.log("deciding:",arg1,arg2);
       const timer = setTimeout(function(){cb([arg1,arg2,"ouch"].join("-"));},5000);
       
       setTimeout(function(){
           if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
               console.log("complete:",arg1,arg2);
               clearTimeout(timer);
               cb(undefined,[arg1,arg2,"all good"].join("-"));
           }
       },2000);
        
    },local);
    

or code new functions, which embed a wrapper.

     function sampleFunc3(arg1,arg2) {return asPromise(arguments,function(resolve,reject){
       console.log("deciding:",arg1,arg2);
       const timer = setTimeout(function(){reject([arg1,arg2,"ouch"].join("-"));},5000);
       
       setTimeout(function(){
           if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
               console.log("complete:",arg1,arg2);
               clearTimeout(timer);
               resolve([arg1,arg2,"all good"].join("-"));
           }
       },2000);
        
    });}
    
    function sampleFunc4(arg1,arg2) {return asCallback(arguments,function(cb){
       console.log("deciding:",arg1,arg2);
       const timer = setTimeout(function(){cb([arg1,arg2,"ouch"].join("-"));},5000);
       
       setTimeout(function(){
           if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
               console.log("complete:",arg1,arg2);
               clearTimeout(timer);
               cb(undefined,[arg1,arg2,"all good"].join("-"));
           }
       },2000);
        
    });}

test scipt for above functions


    const local = {}; 
    promiseToCB(function sampleFunc1(arg1,arg2) {
        console.log("deciding:",arg1,arg2);
        return new Promise(function(resolve,reject){
       
           const timer = setTimeout(function(){reject([arg1,arg2,"ouch"].join("-"));},5000);
           
           setTimeout(function(){
               if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
    
                   console.log("complete:",arg1,arg2);
                   clearTimeout(timer);
                   resolve([arg1,arg2,"all good"].join("-"));
               }
           },2000);
        
        });
    });
    
    cbToPromise('sampleFunc2',function someOtherName(arg1,arg2,cb) {
       console.log("deciding:",arg1,arg2);
       const timer = setTimeout(function(){cb([arg1,arg2,"ouch"].join("-"));},5000);
       
       setTimeout(function(){
           if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
               console.log("complete:",arg1,arg2);
               clearTimeout(timer);
               cb(undefined,[arg1,arg2,"all good"].join("-"));
           }
       },2000);
        
    },local);
    
    function sampleFunc3(arg1,arg2) {return asPromise(arguments,function(resolve,reject){
       console.log("deciding:",arg1,arg2);
       const timer = setTimeout(function(){reject([arg1,arg2,"ouch"].join("-"));},5000);
       
       setTimeout(function(){
           if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
               console.log("complete:",arg1,arg2);
               clearTimeout(timer);
               resolve([arg1,arg2,"all good"].join("-"));
           }
       },2000);
        
    });}
    
    function sampleFunc4(arg1,arg2) {return asCallback(arguments,function(cb){
       console.log("deciding:",arg1,arg2);
       const timer = setTimeout(function(){cb([arg1,arg2,"ouch"].join("-"));},5000);
       
       setTimeout(function(){
           if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
               console.log("complete:",arg1,arg2);
               clearTimeout(timer);
               cb(undefined,[arg1,arg2,"all good"].join("-"));
           }
       },2000);
        
    });}
    
    const log=console.log.bind(console),info=console.info.bind(console),error=console.error.bind(console);
    
    sampleFunc1("sample1","promise").then (log).catch(error);
    local.sampleFunc2("sample2","promise").then (log).catch(error);
    sampleFunc3("sample3","promise").then (log).catch(error);
    sampleFunc4("sample4","promise").then (log).catch(error);

    sampleFunc1("sample1","callback",info);
    local.sampleFunc2("sample2","callback",info);
    sampleFunc3("sample3","callback",info);
    sampleFunc4("sample4","callback",info);
    
    sampleFunc1("sample1","promise-pass").then (log).catch(error);
    local.sampleFunc2("sample2","promise-pass").then (log).catch(error);
    sampleFunc3("sample3","promise-pass").then (log).catch(error);
    sampleFunc4("sample4","promise-pass").then (log).catch(error);

    sampleFunc1("sample1","callback-pass",info);
    local.sampleFunc2("sample2","callback-pass",info);
    sampleFunc3("sample3","callback-pass",info);
    sampleFunc4("sample4","callback-pass",info);
    
    
    sampleFunc1("sample1","promise-fail").then (log).catch(error);
    local.sampleFunc2("sample2","promise-fail").then (log).catch(error);
    sampleFunc3("sample3","promise-fail").then (log).catch(error);
    sampleFunc4("sample4","promise-fail").then (log).catch(error);
    
    sampleFunc1("sample1","callback-fail",info);
    local.sampleFunc2("sample2","callback-fail",info);
    sampleFunc3("sample3","callback-fail",info);
    sampleFunc4("sample4","callback-fail",info);
 
    var cpArgs = Array.prototype.slice.call.bind(Array.prototype.slice);

    function promiseToCB (nm,fn,THIS) {
        if (typeof nm==='function') {
            THIS=fn;fn=nm;nm=fn.name;
        }
        THIS=THIS||this;
        const func = function () {
           let args = cpArgs(arguments);
            if (typeof args[args.length-1]==='function') {
                const cb = args.pop();
                return fn.apply(THIS,args).then(function(r){
                   cb (undefined,r);
                }).catch(cb);  
            } else {
                return fn.apply(THIS,args);
            }
        };
        Object.defineProperty(func,'name',{value:nm,enumerable:false,configurable: true});
        if (THIS[nm]) delete THIS[nm];
        Object.defineProperty(THIS,nm,{value:func,enumerable:false,configurable: true});
        return func;
    }

    function cbToPromise (nm,fn,THIS) {
        if (typeof nm==='function') {
            THIS=fn;fn=nm;nm=fn.name;
        }
        THIS=THIS||this;
        const func = function () {
           let args = cpArgs(arguments);
            if (typeof args[args.length-1]==='function') {
                return fn.apply(THIS,args);
            } else {
                return new Promise(function(resolve,reject){
                    
                    args.push(function(err,result){
                          if (err) return reject(err);
                          if (arguments.length==2) {
                             return resolve(result);
                          }
                          return resolve(cpArgs(arguments,1));
                    });
                              
                    fn.apply(THIS,args);
                    
                });
            }
        };
        Object.defineProperty(func,'name',{value:nm,enumerable:false,configurable: true});
        if (THIS[nm]) delete THIS[nm];
        Object.defineProperty(THIS,nm,{value:func,enumerable:false,configurable: true});
        return func;

    }

    function asPromise (args,resolver,no_err) {
        const cb = args[args.length-1],
        promise  = new Promise(resolver);
        return (typeof cb==='function')  ? promise.then(function(result){return cb(no_err,result)}).catch(cb) : promise;
    }

    function asCallback (args,wrap,no_err) {
        const cb = args[args.length-1],
        promise=new Promise(function resolver(resolve,reject) {
            return wrap (function (err,result) {
                 if (err) return reject(err);
                 resolve(result);
            });
        });
        return (typeof cb==='function')  ? promise.then(function(result){return cb(no_err,result)}).catch(cb) : promise;
    }


    function cbPromiseTest(){
        /*global sampleFunc1,sampleFunc2*/
        
        const local = {}; 
        promiseToCB(function sampleFunc1(arg1,arg2) {
            console.log("deciding:",arg1,arg2);
            return new Promise(function(resolve,reject){
           
               const timer = setTimeout(function(){reject([arg1,arg2,"ouch"].join("-"));},5000);
               
               setTimeout(function(){
                   if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
        
                       console.log("complete:",arg1,arg2);
                       clearTimeout(timer);
                       resolve([arg1,arg2,"all good"].join("-"));
                   }
               },2000);
            
            });
        });
        
        cbToPromise('sampleFunc2',function someOtherName(arg1,arg2,cb) {
           console.log("deciding:",arg1,arg2);
           const timer = setTimeout(function(){cb([arg1,arg2,"ouch"].join("-"));},5000);
           
           setTimeout(function(){
               if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
                   console.log("complete:",arg1,arg2);
                   clearTimeout(timer);
                   cb(undefined,[arg1,arg2,"all good"].join("-"));
               }
           },2000);
            
        },local);
        
        function sampleFunc3(arg1,arg2) {return asPromise(arguments,function(resolve,reject){
           console.log("deciding:",arg1,arg2);
           const timer = setTimeout(function(){reject([arg1,arg2,"ouch"].join("-"));},5000);
           
           setTimeout(function(){
               if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
                   console.log("complete:",arg1,arg2);
                   clearTimeout(timer);
                   resolve([arg1,arg2,"all good"].join("-"));
               }
           },2000);
            
        });}
        
        function sampleFunc4(arg1,arg2) {return asCallback(arguments,function(cb){
           console.log("deciding:",arg1,arg2);
           const timer = setTimeout(function(){cb([arg1,arg2,"ouch"].join("-"));},5000);
           
           setTimeout(function(){
               if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
                   console.log("complete:",arg1,arg2);
                   clearTimeout(timer);
                   cb(undefined,[arg1,arg2,"all good"].join("-"));
               }
           },2000);
            
        });}
        
        const log=console.log.bind(console),info=console.info.bind(console),error=console.error.bind(console);
        
        sampleFunc1("sample1","promise").then (log).catch(error);
        local.sampleFunc2("sample2","promise").then (log).catch(error);
        sampleFunc3("sample3","promise").then (log).catch(error);
        sampleFunc4("sample4","promise").then (log).catch(error);

        sampleFunc1("sample1","callback",info);
        local.sampleFunc2("sample2","callback",info);
        sampleFunc3("sample3","callback",info);
        sampleFunc4("sample4","callback",info);
        
        sampleFunc1("sample1","promise-pass").then (log).catch(error);
        local.sampleFunc2("sample2","promise-pass").then (log).catch(error);
        sampleFunc3("sample3","promise-pass").then (log).catch(error);
        sampleFunc4("sample4","promise-pass").then (log).catch(error);

        sampleFunc1("sample1","callback-pass",info);
        local.sampleFunc2("sample2","callback-pass",info);
        sampleFunc3("sample3","callback-pass",info);
        sampleFunc4("sample4","callback-pass",info);
        
        
        sampleFunc1("sample1","promise-fail").then (log).catch(error);
        local.sampleFunc2("sample2","promise-fail").then (log).catch(error);
        sampleFunc3("sample3","promise-fail").then (log).catch(error);
        sampleFunc4("sample4","promise-fail").then (log).catch(error);
        
        sampleFunc1("sample1","callback-fail",info);
        local.sampleFunc2("sample2","callback-fail",info);
        sampleFunc3("sample3","callback-fail",info);
        sampleFunc4("sample4","callback-fail",info);
     
    }
    cbPromiseTest();

Leave a Comment