简体   繁体   中英

How to synchronously call a set of functions in javascript

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:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM