Function within function 3 times over in Javascript

Viewed 71

I am kinda new to Javascript and have written some Chrome Extension. The code contains a sequence process where one function is being passed to another and will be called when the first is done. To be frank, this is getting complicated for me to even look at.

I will explain with an example that shows the sequence I have in my code:

function successFunc() {
    console.log("Success!");
}

function handleErrorFunc(successFunc) {
    ... stuff to handle error...
    step1(successFunc);
}

function step1(successFunc) {
    var url = ...
    var request = new XMLHttpRequest();
    request.open("GET", url, true);
    request.send();
    request.onerror = function() {
        ...
    }
    request.onreadystatechange = function() {
        if (request.readyState == 4 && request.status == 200) {
            ...
            step2(successFunc)
        }
    }
}


function step2(successFunc) {
    var url = ...
    var request = new XMLHttpRequest();
    request.open("GET", url, true);
    request.send();
    request.onerror = function() {
        ...
    }
    request.onreadystatechange = function() {
        if (request.readyState == 4 && request.status == 200) {
            ...
            successFunc()
        }
    }
}

Now I call

step1(successFunc);

Is there something to prevent this from happening, some design pattern perhaps?

3 Answers

Many javascript libraries return a Promise from an ajax call. This is a preferable method to using callbacks as it neatens your code considerable.

Instead of the code you currently have, your code would instead look like this.

step1().then(step1Result => step2(step1Result))
      .catch(err => console.error("step1 faled",err));

You can continue to chain this as appropriate

step1().then(step1Result => step2(step1Result)
                             .then(step2Result => step3(step2Result))
                             .catch(err => console.error("step2 failed",err))
      )
      .catch(err => console.error("step1 faled",err));

Using the standard XMLHttpRequest does not follow this pattern, but it is straightforward to wrap in a Promise if thats what you want to do - however it is much easier to use an existing ajax library which supports promises.

I would suggest you start by reading Using Promises guide.


edit: If you want to wrap your calls in an Promise make sure you pass back the response as well as the errors. eg:

function ajaxGet(url){
     return new Promise((resolve, reject) => {
        var request = new XMLHttpRequest();
        request.open("GET", url, true);
        request.send();
        request.onerror = reject; // will pass back error
        request.onreadystatechange = function() {
            if (request.readyState == XMLHttpRequest.DONE && request.status == 200) {
                resolve(request.responseText);
            }
        }
    });
}

Then step1 might be

function step1(){
    return ajaxGet("url/for/step1");
}

What you want to look at is promises in Javascript. But before you dive into something that specific I would recommend you get a basic understanding of functional programming in Javascript.

A few links to help you out in this case are given below -

Functional Programming in JS

Promises in JS

A better approach to your problem is to use promises :

function successFunc() {
    console.log("Success!");
}

// now returns a promise and does not need successFunc
function step1() {
    return new Promise((resolve, reject) => {
        const request = new XMLHttpRequest();
        request.open("GET", 'your/url', true);
        request.send();
        request.onerror = function() {
            reject();
        }
        request.onreadystatechange = function() {
            if (request.readyState == 4 && request.status == 200) {
                resolve();
            }
        }
    });
}


// now returns a promise and does not need successFunc
function step2() {
    return new Promise((resolve, reject) => {
        var request = new XMLHttpRequest();
        request.open("GET", 'your/url', true);
        request.send();
        request.onerror = function() {
            reject();
        }
        request.onreadystatechange = function() {
            if (request.readyState == 4 && request.status == 200) {
                resolve();
            }
        }
    });
}

function handleErrorFunc(step) {
    if (step === 1) {
        // handle step1 errors...
    }
    else if (step === 2) {
        // handle step2 errors...
    }
}

// chain your step calls
step1()
.then(() => {
    step2()
    .then(() => {
        successFunc();
    })
    .catch(() => {
        handleErrorFunc(2);
    });
})
.catch(() => {
    handleErrorFunc(1);
});
Related