Skip to content Skip to sidebar Skip to footer

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

Solution 1:

Use promises:

//The set of functions that I want to call in orderfunctiongetData(initialData) {
  //gets the datareturnnewPromise(function (resolve, reject) {
    resolve('Hello World!')
  })
}

functionparseData(dataFromGetDataFunction) {
  //does some stuff with the datareturnnewPromise(function (resolve, reject) {
    resolve('Hello World!')
  })
}

functionvalidate(dataFromParseDataFunction) {
  //validates the datareturnnewPromise(function (resolve, reject) {
    resolve('Hello World!')
  })
}

//The function that orchestrates these calls functionrunner(initialData) {
    returngetData(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 datacallback(/*...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:

functiongetData(callback) {
    returnnewPromise(function(resolve, reject) {
        someAsyncOperation(function() {
            // Async is done now, call the callback with the dataresolve(/*...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";

functiongetData() {
    // Return a promisereturnnewPromise((resolve, reject) => {
        setTimeout(() => {
            // Let's fail a third of the timeif (Math.random() < 0.33) {
                reject("getData failed");
            } else {
                resolve('{"msg":"This is the message"}');
            }
        }, Math.random() * 100);
    });
}

functionparseData(data) {
    // Note that this function is synchronousreturnJSON.parse(data);
}

functionvalidate(data) {
    // Let's assume validation is synchronous too// Let's also assume it fails half the timeif (!data || !data.msg || Math.random() < 0.5) {
        thrownewError("validation failed");
    }
    // It's finereturn data;
}

functionrunner() {
    returngetData()
        .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
);
<inputtype="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:

functiongetData() {
  returnnewPromise(function (res, rej) {
    var req = newAjaxRequest(...); // 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:

Post a Comment for "How To Synchronously Call A Set Of Functions In Javascript"