How To Synchronously Call A Set Of Functions In Javascript
Solution 1:
Use promises:
//The set of functions that I want to call in order
function getData(initialData) {
//gets the data
return new Promise(function (resolve, reject) {
resolve('Hello World!')
})
}
function parseData(dataFromGetDataFunction) {
//does some stuff with the data
return new Promise(function (resolve, reject) {
resolve('Hello World!')
})
}
function validate(dataFromParseDataFunction) {
//validates the data
return new Promise(function (resolve, reject) {
resolve('Hello World!')
})
}
//The function that orchestrates these calls
function runner(initialData) {
return getData(initialData)
.then(parseData)
.then(validate)
}
runner('Hello World!').then(function (dataFromValidateFunction) {
console.log(dataFromValidateFunction);
})
Not only are they easy to grasp, it makes total sense from a code readability stand point. Read more about them here. If you are in a browser environment, I recommend this polyfill.
Solution 2:
The code you've quoted will run synchronously. JavaScript function calls are synchronous.
So I'm going to assume that getData
, parseData
, and/or validate
involve asynchronous operations (such as using ajax in a browser, or readFile
in NodeJS). If so, you basically have two options, both of which involve callbacks.
The first is to just have those functions accept callbacks they'll call when done, for instance:
function getData(callback) {
someAsyncOperation(function() {
// Async is done now, call the callback with the data
callback(/*...some data...*/);
});
}
you'd use that like this:
getData(function(data) {
// Got the data, do the next thing
});
The problem with callbacks is that they're hard to compose and have fairly brittle semantics. So promises were invented to give them better semantics. In ES2015 (aka "ES6") or with a decent promises library, that would look something like this:
function getData(callback) {
return someAsyncOperation();
}
or if someAsyncOperation
is not promise-enabled, then:
function getData(callback) {
return new Promise(function(resolve, reject) {
someAsyncOperation(function() {
// Async is done now, call the callback with the data
resolve(/*...some data...*/);
// Or if it failed, call `reject` instead
});
});
}
Doesn't seem to do much for you, but one of the key things is composability; your final function ends up looking like this:
function runner() {
return getData()
.then(parseData) // Yes, there really aren't () on parseData...
.then(validate); // ...or validate
}
usage:
runner()
.then(function(result) {
// It worked, use the result
})
.catch(function(error) {
// It failed
});
Here's an example; it will only work on a fairly recent browser that supports Promise
and ES2015 arrow functions, because I was lazy and wrote it with arrow functions and didn't include a Promise lib:
"use strict";
function getData() {
// Return a promise
return new Promise((resolve, reject) => {
setTimeout(() => {
// Let's fail a third of the time
if (Math.random() < 0.33) {
reject("getData failed");
} else {
resolve('{"msg":"This is the message"}');
}
}, Math.random() * 100);
});
}
function parseData(data) {
// Note that this function is synchronous
return JSON.parse(data);
}
function validate(data) {
// Let's assume validation is synchronous too
// Let's also assume it fails half the time
if (!data || !data.msg || Math.random() < 0.5) {
throw new Error("validation failed");
}
// It's fine
return data;
}
function runner() {
return getData()
.then(parseData)
.then(validate);
}
document.getElementById("the-button").addEventListener(
"click",
function() {
runner()
.then(data => {
console.log("All good! msg: " + data.msg);
})
.catch(error => {
console.error("Failed: ", error && error.message || error);
});
},
false
);
<input type="button" id="the-button" value="Click to test">
(you can test more than once)
Solution 3:
You should change each function to return a Promise
, which will allow your final function to become:
function runner() {
return Promise.try(getData).then(parseData).then(validate);
}
To do that, the body of each function should be wrapped in a new promise, like:
function getData() {
return new Promise(function (res, rej) {
var req = new AjaxRequest(...); // make the request
req.onSuccess = function (data) {
res(data);
};
});
}
This is a very cursory example of how promises might work. For more reading, check out:
- 2ality's fantastic blog posts: part 1 and part 2
- bluebird's documentation on why promises
- mdn's documentation on JS'
Promise
class
Post a Comment for "How To Synchronously Call A Set Of Functions In Javascript"