Continuation-passing style, ie "callbacks", and Promises are two differing kinds of machinery for handling asynchronous code. Historically, JavaScript and Node used Function callbacks and the programmer was expected to follow certain conventions to write their code. Because it's hard to follow an unenforced convention, writing callback code was notoriously prone to error. Promises are a newer first-class data structure that offers more features specifically designed for handling asynchrony.
if you write getValue1,
The JavaScript community at large has widely accepted Promises and even more features have been added to the language to take advantage of their ubiquity. You should be writing Promise-based code wherever you can. That means if you are the writer of getValue1
, it should not take a callback, and instead return a promise -
const delay = (ms, value) =>
new Promise(r => setTimeout(r, ms, value))
const getValue1 = _ =>
delay(2000, "B")
getValue1().then(console.log, console.error)
console.log("please wait...")
You can write getValue1
using continuation-passing style, if you wish. Node.js uses a convention where any error, if present, will be the first argument to the callback. In Node, you must always check for the presence of an error in your callback, otherwise you may be introducing bugs in your program. To follow this convention, your code would look like -
const getValue1 = (callback) =>
setTimeout(_ => callback(null, "B"), 2000)
getValue1((err, res) => {
if (err)
console.error("error encountered:", err.message)
else
console.log("result:", res)
})
console.log("please wait...")
if you inherit getValue1,
When you inherit callback style function it's beneficial to convert it so you can use it with newer Promise-based code. Node.js has a built-in function, util.promisify, to do just that. For demonstration purposes, I included my own promisify
in the example below -
const promisify = f => (...a) => // <- util.promisify
new Promise
( (resolve, reject) =>
f(...a, (e, v) => e ? reject(e) : resolve(v))
)
const getValue1 = (callback) => // <- old code
setTimeout(_ => callback(null, "B"), 2000)
const _getValue1 =
promisify(getValue1) // <- promisify old code
_getValue1().then(console.log, console.error) // <- back to sanity
console.log("please wait...")
buy-in to the Promise
Promises allow you to leverage new language features like async
and await
, dramatically improving the code reading and writing experience -
const sleep = ms =>
new Promise(r => setTimeout(r, ms))
async function getValue1 ()
{ await sleep(1000)
console.log("loading: 25%...")
await sleep(1000)
console.log("loading: 50%...")
await sleep(1000)
console.log("loading: 75%...")
await sleep(1000)
return "B"
}
async function main()
{ console.log("please wait...")
const result = await getValue1()
console.log("done:", result)
}
main().catch(console.error)
please wait...
loading: 25%...
loading: 50%...
loading: 75%...
done: B