Reduce nested ifs in javascript

I have three functions that execute asynchronously when the return function is part of the function call. What I need is "If function A and function B both return a good value (not an error), then execute function C." What I have now is "If function A returns a good value, then call function B, if function B returns a good value, then call function C."

Is there a better way to do this in javascript?

The code I'm working with looks like this:

// Login to amazon
// function 0       
amazon.Login.authorize(options, function(authResponse) {
    if ( authResponse.error ) {
        // user not logged in. Do nothing.
        alert('oauth error ' + response.error);
        return;
    }
    else
    { // good response
      // Call function A
      amazon.Login.retrieveProfile(authResponse.access_token, function(response) {
          if ( response.success ) { // good response
              // Prepare to call function B   
              // Initialize the Amazon Cognito credentials provider
              AWS.config.credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId: 'abc' });
              AWS.config.region = 'us-east-1';
              AWS.config.credentials.params.Logins = { 'www.amazon.com': authResponse.access_token };

              // Call function B. This could be called at the same time as function A                               
              AWS.config.credentials.get(function(err) {
              if (!err) {
                   // Both Function A and Function B came back with good results. 
                   // Now can call function C and do more work
              } else {
                   // error in function B
              }
          } else {
              // error in function A
          }
     }
}

      

+3


source to share


3 answers


It looks like you are building the Pyramid of Doom. You can avoid this by chaining promises (I used a polyfill from https://www.promisejs.org/ ). You can combine as many functions as you like as long as they return promises. Also, you can avoid the block byte else

that used to throw an exception.

var accessToken;

(new Promise(amazon.Login.authorize.bind(amazon.Login, options)))
    .then(function (authResponse) {
        if (authResponse.error) throw res.error;

        accessToken = authResponse.access_token;

        return new Promise(amazon.Login.retrieveProfile.bind(amazon.Login, accessToken));
    })
    .then(function (response) {
        if (!response.success) throw 'Profile retrieval failed';

        AWS.config.credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId: 'abc' });
        AWS.config.region = 'us-east-1';
        AWS.config.credentials.params.Logins = { 'www.amazon.com': accessToken };

        return new Promise(AWS.config.credentials.get.bind(AWS.config.credentials));
    })
    .then(function (err) {
        if (err) throw err;

        // call function C here
    })
    .catch(function (err) {
        // do something with error
    });

      



You might want to check the documentation for the API used. There may be versions of those methods that promises return already.

Demo: http://jsbin.com/tovokarole/1/edit?js,console

+1


source


You could reduce the complexity by not adding these functions in a row, but rather by defining them externally. In this example amazon.Login.authorize (options, authorizeCallback (response));

function authorizeCallback(response) {
    if ( authResponse.error ) {
        alert('oauth error ' + response.error);
        return;
    }
    amazon.Login.retrieveProfile(authResponse.access_token, retrieveProfileCallback);
}

function retrieveProfileCallback(response) {
    if ( response.success ) { // good response
      // Prepare to call function B   
      // Initialize the Amazon Cognito credentials provider
      AWS.config.credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId: 'abc' });
      AWS.config.region = 'us-east-1';
      AWS.config.credentials.params.Logins = { 'www.amazon.com': authResponse.access_token };

      // Call function B. This could be called at the same time as function A                               
      AWS.config.credentials.get(getCredentialCallback);

  } else {
      // error in function A
  }
}

function getCredentialCallback(err) {
    if (!err) {
        // Both Function A and Function B came back with good results. 
        // Now can call function C and do more work
    } else {
        // error in function B
    }
}

      



And so on, javascript treats functions as first class citizens, so you can treat them as values โ€‹โ€‹and pass them around.

0


source


as Gustavo demonstrated, you can define inline functions outside and then reference it. I would recommend this for your example.

But that doesn't diminish your nested conditionals. Sure, the authorization function looks shorter, but it always has nested conventions.

If you have more and more nested conditionals than your search keyword should be "Asynchronous Control Flow Patterns". Then you will find libraries that handle this asynchronous encoding.

Take a look at the async library.

https://github.com/caolan/async

0


source







All Articles