Skip to content

A utility package for converting different kinds of javascript callbacks into various promise types.

License

Notifications You must be signed in to change notification settings

antoniormrzz/callback-bender

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

callback-bender

npm version Patreon donate button
A utility package for converting different kinds of javascript callbacks into various promise types. You can wrap functions that work with callbacks into functions that return promises and can be used with async/await. If this does help you, please consider making a tiny donation here. 🤝

About Project

Working with javascript can be frustrating for many reasons, one of them is the callback hell. In addition to callbacks making code uglier and longer, they also make it hard to work with promises or async/await. The goal of this project is to provide wrapper functions that transform these functions into functions that return native promises.

Most Common Use Cases For Callback Converters

When working with NodeJS, you will encounter many Node API functions that work with callbacks. for example, many if not all functions in the 'fs' module use EFC style callbacks. Though there is a utility package for converting them, the utility package is part of nodejs and not available on other platforms. On the other hand, a lot of the examples online do not use the promisified syntax.

Why Callback Bender?

  • Uses standard, friendly syntax const wrapped = bend.efc.single(fs.readdir);
  • Unified syntax across all your projects
  • Supports both Continuation Passing callbacks (normal) and Error-First callbacks (EFC or node style)
  • Supports wrapping callbacks that accept multiple/single/no argument(s)
  • Is pretty small (3KB even before minification)
  • Provides many module formats (.mjs, es6, IIFE or script tag, all in the lib folder)
  • Provides type definitions
  • Provides typescript version (lib folder)
  • Is tested and passing all tests

Callback Types

There are mainly two types of callbacks, CP and EFC.

CP:

Continuation Passing callbacks are callbacks that do not accept errors (at least not as the first parameter).

// API code:
function foo(a, b, cb){
    .
    .
    .
    cb(a+b);
}

// your code:
foo(1,2,(res)=>{...});

EFC:

Error-First callbacks are callbacks that accept error as their first parameter, this style is most commonly used in NodeJS.

// API code:
function foo(a, b, cb){
    .
    .
    .
    if(error){
    cb(error);
    }else{
    cb(undefined, a+b);
    }
}

// your code:
foo(1,2,(err, res)=>{
    if(err){
    ...
    } else {
    ...
    }
});

Getting Started

This library uses and depends on es6, so keep that in mind. There are no package dependencies.

npm : 

note: the default file in the package uses CommonJS (require style, mainly for node), if you need ES6 Module version (try this if in doubt) or .mjs or IIFE (var name is callback_bender) please import or use files from lib folder!

npm i callback-bender

Format:

<bend.callback_type.return_count>

example: bend.cp.multiple(...)
         bend.efc.none(...)

example: const wrapped = bend.efc.single(fs.readdir);

note: CP is aliased as normal e.g. bend.normal.none(...) , EFC is aliased as node e.g. bend.node.none(...)

Why are there different versions of return types?

A callback can accept more than one argument, e.g. (a,b,c) => {} but async/await format expects one return to store in your variable,  e.g. let foo = await something(); This is why we need to use different wrappers for different callbacks.

Argument countCPEFCWhat you Should UseResolves toexample (efc)
0()(err)nonevoidbend.efc.none(myfunction)(1, 2, 3).then(() => {...});
1(res)(err, res)singleresbend.efc.single(myfunction)(1, 2, 3).then((res) => {...});
more than 1(res1, res2)(err, res1, res2)multipleobject (can be named)

bend.efc.multiple(myfunction)(1, 2, 3).then(({1:res1,2:res2}) => {...});

bend.efc.multiple(myfunction,['first','second'])(1, 2, 3).then(({first:res1,second:res2}) => {...});

Now that we covered the basics, let's see some examples:

import:

from the default file in package:

const bend = require('callback_bender');
// import bend from 'callback_bender/lib/cb.es6'; for es6 or browser
// <script src="callback_bender/lib/cb.iife.js"></script> you should use callback_bender.efc.none etc
// .ts file and .mjs file are also in the lib folder

Let's say we have a function as follows:

function ugly(efc) {
      // some code that will at some point call your callback with error or result
    };

Normally we would use this function as follows:

ugly((err, res)=>{ 
    if(err){ 
    handleError(err);
    } else{
    doSomething(res);
    }
 });

The callback is an error first callback, and takes one other argument. so we should use .efc.single() or .node.single():

// async/await style
try{
const res = await bend.efc.single(ugly)();
doSomething(res);
}catch(err){
handleError(err);
}

// promise style
bend.efc.single(ugly)().then(res => { doSomething(res); }).catch(err => { handleError(err); };

Let's see an example of none and CP, the API function would probably be something like:

function ugly(a, b, c, cp) => {
 // please note that a, b, c are not what we count as argument counts
      // this function calls cb() with no arguments or throws an error
    };

Normally we would use this function as follows:

ugly(()=>{ doSomething(); });

The callback is a normal callback, or CP, ( you can note that there is no error checking in callback ), and takes zero arguments. so we should use .cp.none() or .normal.none():

// async/await style
try{
await (bend.cp.none(ugly)(a,b,c,d));
doSomething();
}catch(err){ // the function might throw, so it's better to check
handleError(err);
}

// promise style
bend.cp.none(ugly)(a,b,c,d).then(() => { doSomething(); }).catch(err => { handleError(err); };

Let's see an example of multiple and efc, the API function would probably be something like:

function ugly(..., efc) {
 // function takes a callback
      // at some point the function will either call efc(err) or efc(undefined, res1, res2) so this callback will get more than one results
    };

Normally we would use this function as follows:

ugly((err, res1, res2)=>{ 
    if(err){ 
    handleError(err);
    } else{
    doSomething(res1);
    doStuff(res2);
    }
 });

The callback is an error first callback, and takes more than one argument (error doesn't count). so we should use .efc.multiple() or .node.multiple():

// async/await style
try{
const obj = await bend.efc.multiple(ugly)();
doSomething(obj[1]); 
doStuff(obj[2]);
}catch(err){
handleError(err);
}

// promise style
bend.efc.multiple(ugly)().then(obj => { doSomething(obj[1]); doStuff(obj[2]); }).catch(err => { handleError(err); };

Naming the .multiple object:

In the previous example, we can name the parameters on the object, by supplying an optional string array:

// async/await style
try{
const obj = await bend.efc.multiple(ugly,['first','second'])();
doSomething(obj.first); 
doStuff(obj.second);
}catch(err){
handleError(err);
}

// promise style
bend.efc.multiple(ugly,['first','second'])().then(obj => { doSomething(obj.first); doStuff(obj.second); }).catch(err => { handleError(err); };

A NodeJS fs example:

fs.readdir(path.join(...), (err, files) => {
            if(err){
            ...
            }else{
            ...
            }
          });

We are using an EFC (usual for node), and we are getting one argument (files) so:

const wrapped = bend.efc.single(fs.readdir);
wrapped(path.join(...)).then(res => ... ).catch(err => ... );

License

Distributed under the MIT License. See LICENSE for more information.

Contact

Antonio Ramirez: sepehralizade@live.com

Project Link: Github

About

A utility package for converting different kinds of javascript callbacks into various promise types.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published