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 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:


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