One of the simplest ways to make functions asynchronous is to accept a callback function. This is what node.js does (at time of writing). This works, but has a number of issues.
- You lose the separation of inputs and outputs to a function since the callback must be passed as an input
- It is difficult to compose multiple serial or parallel operations
- You lose a lot of helpful debugging information and error handling ability relating to stack traces and the bubbling up of exceptions
- You can no longer use the built in control flow constructs and they must all be re-invented to work asynchronously.
Many APIs in the browser use some kind of event based model for control flow, which solves problem 1, but not problems 2 to 4.
Promises aim to solve issues 1 to 3 and can solve problem 4 in ES6 (with the use of generators).