I am working on a javascript project that needs to get some data and process it, but I am having trouble with the asynchronous nature of JavaScript. What I want to be able to do is something like the following.
//The set of functions that I want to call in order
function getData() {
//gets the data
}
function parseData() {
//does some stuff with the data
}
function validate() {
//validates the data
}
//The function that orchestrates these calls
function runner() {
getData();
parseData();
validate();
}
Here I want each function to wait for completion before going on to the next call, as I am running into the situation where the program attempts to validate the data before it has been retrieved. However, I also want to be able to return a value from these functions for testing, so I can't have these functions return a boolean value to check completion. How can I make javascript wait on the function to run to completion before moving on to the next call?
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.
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)
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:
Promise
class
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.