Originally, callbacks were used for asynchronous operations (e.g., in the XMLHttpRequest API). Now promise-based APIs like the browser's Fetch API have become the default solution and the nicer async/await
syntax is supported by all modern browsers and on Node.js (server side).
A common scenario - fetching JSON data from the server - can look like this:
async function fetchResource(url) { const res = await fetch(url); if (!res.ok) { throw new Error(res.statusText); } return res.json();}
To use it in another function:
async function doSomething() { try { const data = await fetchResource("https://example.test/resource/1"); // ... } catch (e) { // Handle error ... }}
If you design a modern API, it is strongly recommended to prefer promise-based style over callbacks. If you inherited an API that relies on callbacks, it is possible to wrap it as a promise:
function sleep(timeout) { return new Promise((resolve) => { setTimeout(() => { resolve(); }, timeout); });}async function fetchAfterTwoSeconds(url) { await sleep(2000); return fetchResource(url);}
In Node.js, which historically relied exclusively on callbacks, that technique is so common that they added a helper function called util.promisify
.