Using Conditions in AngularJS: Choosing Between Callbacks and Promises in a Service

I am currently faced with a specific scenario where I am uncertain whether to implement a callback or a promise. As someone who is relatively new to promises and just beginning to grasp their concept, I want to avoid falling into any potential anti patterns.

I have thoroughly studied the Angular documentation on $q.

Here is what I aim to achieve:

If using promises :

OauthService.token().then(function(access_token){
    config.url = config.url+'?access_token='+access_token;
});

return config;

If using callback :

OauthService.token(function(access_token){
    config.url = config.url+'?access_token='+access_token;
});

return config;

The Oauth service contains some conditions that make me lean towards using callbacks instead of promises.

Therefore, my service currently utilizes callbacks.

OauthService.js :

app.factory('OauthService', function($http, $localStorage) {

return {
    token : function(callback){

        // Retrieve current token
        access_token = $localStorage.getObject('access_token');
        // Retrieve current identity
        identity_token = $localStorage.getObject('identity_token');

        // If no user is logged in
        if(isObjectEmpty(identity_token)){

            // If access_token does NOT exist OR will expire soon
            if( isObjectEmpty(access_token) || Date.now() > (access_token.expires_at - (600*1000)) ){

                // Create an anonymous access_token
                $http
                    .get(domain+'/oauth/v2/token?client_id='+public_id+'&client_secret='+secret+'&grant_type=client_credentials')
                    .then(function (response) {

                        $localStorage.setObject('access_token', {
                            key: response.data.access_token,
                            type: 'anonymous',
                            expires_at: Date.now()+(response.data.expires_in*1000)
                        });

                        // return access token here
                        callback(response.data.access_token);

                    });
            }

        }
        // If user is logged in
        else{

            // If access_token does NOT exist OR will expire soon OR is anonymous
            if( isObjectEmpty(access_token) || Date.now() > (access_token.expires_at - (600*1000)) || access_token.type == 'anonymous' ){
                // Create an access_token with an identity
                $http
                    .get(domain+'/oauth/v2/token?client_id='+public_id+'&client_secret='+secret+'&api_key='+identity_token+'&grant_type=http://oauth2.dev/grants/api_key')
                    .then(function (response) {

                        $localStorage.setObject('access_token', {
                            key: response.data.access_token,
                            type: 'identity',
                            expires_at: Date.now()+(response.data.expires_in*1000)
                        });

                        // return access token here
                        callback(response.data.access_token);

                    });
            }

        }

        // return access token here (if the previous token has not changed type or expired)
        callback(access_token.key);

    }
};

})

If I were to opt for promises, how should I go about implementing that?

Answer №1

When it comes to executing operations, the presence of conditions is unrelated to whether you choose callbacks or promises. Using plain callbacks for asynchronous operations is considered subpar, and leveraging promises whenever possible is recommended.

To enhance your token method with promises, you can implement the following approach:

app.factory('OauthService', function($http, $localStorage, $q) {
    return {
        token : function(callback){

            // Retrieving current token
            access_token = $localStorage.getObject('access_token');
            // Fetching current identity
            identity_token = $localStorage.getObject('identity_token');

            // If no user is logged in
            if(isObjectEmpty(identity_token)){

                // If access_token doesn't exist or is expiring soon
                if( isObjectEmpty(access_token) || 
                    Date.now() > (access_token.expires_at - (600*1000)) ){

                    // Generating an anonymous access_token using a promise
                    return $http
                        .get(domain+'/oauth/v2/token?client_id='+public_id + 
                             '&client_secret=' + secret + '&grant_type=client_credentials')
                        .then(function (response) {
                            $localStorage.setObject('access_token', {
                                key: response.data.access_token,
                                type: 'anonymous',
                                expires_at: Date.now() + 
                                       (response.data.expires_in * 1000)
                            });

                            return response.data.access_token;
                        });
                }
            }

            // If user is logged in
            else {
                // Processing when access_token is missing, about to expire, or is of anonymous type
                if( isObjectEmpty(access_token) || 
                    Date.now() > (access_token.expires_at - (600*1000)) || 
                    access_token.type == 'anonymous' ){

                    // Creating an access_token incorporating identity details through a promise
                    return $http
                        .get(domain+'/oauth/v2/token?client_id='+public_id+'&client_secret='+secret + 
                             '&api_key='+identity_token+'&grant_type=http://oauth2.dev/grants/api_key')
                        .then(function (response) {
                            $localStorage.setObject('access_token', {
                                key: response.data.access_token,
                                type: 'identity',
                                expires_at: Date.now()+
                                      (response.data.expires_in * 1000)
                            });

                            return response.data.access_token;    
                        });
                }

            }

            // Return existing access token (if previous token remains unchanged in type or hasn't expired)
            return $q.when(access_token.key);    
        }
    };
});

You can streamline the code further by refactoring it as shown below:

app.factory('OauthService', function($http, $localStorage, $q) {
    function expiresSoon(access_token) {
        return Date.now() > (access_token.expires_at - (600*1000));
    }

    function getAccessToken(url, type) {
        return $http
            .get(url)
            .then(function (response) {
                $localStorage.setObject('access_token', {
                    key: response.data.access_token,
                    type: type,
                    expires_at: Date.now() + 
                        (response.data.expires_in * 1000)
            });

                return response.data.access_token;
            });
    }

    return {
        token : function(callback){

            // Retrieve current token
            access_token = $localStorage.getObject('access_token');
            // Retrieve current identity
            identity_token = $localStorage.getObject('identity_token');

            // If no user is logged in
            if(isObjectEmpty(identity_token)){

                // If access_token doesn't exist or is expiring soon
                if( isObjectEmpty(access_token) || expiresSoon(access_token) ) {
                    var url = domain + '/oauth/v2/token?client_id=' + public_id + 
                              '&client_secret=' + secret + '&grant_type=client_credentials';
                    // Create an anonymous access_token using promises
                    return getAccessToken(url, 'anonymous');
                }
            }

            // If user is logged in
            else {
                // Processing when access_token is missing, about to expire, or is of anonymous type
                if( isObjectEmpty(access_token) || 
                    expiresSoon(access_token) || 
                    access_token.type == 'anonymous' ){

                    var url = domain+'/oauth/v2/token?client_id=' + public_id+
                              '&client_secret='+secret + 
                              '&api_key='+identity_token + 
                              '&grant_type=http://oauth2.dev/grants/api_key';

                    // Creating an access_token with identity using promises
                    return getAccessToken(url, 'identity');
                }
            }

            // Return existing access token (if previous token remains unchanged in type or hasn't expired)
            return $q.when(access_token.key);    
        }
    };
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Typescript and Mongoose Schema - Common Design Mistakes

I am encountering two issues while defining a schema using mongoose and typescript. Below is the code snippet I'm having trouble with: import { Document, Schema, Model, model} from "mongoose"; export interface IApplication { id: number; name ...

What is the best way to implement colorful opening hours in code?

I found this code on a different platform and decided to tweak it to display only Monday - Friday, Saturday, and Sunday. However, I'm stuck trying to update the Javascript code to match my modifications. Any help would be appreciated! You can view th ...

Menu becomes sticky too quickly on iOS Safari and Chrome, jumping to fixed position prematurely

Feeling frustrated with this seemingly straightforward code challenge. My sticky menu is causing me headaches on iOS devices, particularly the iPhone 6 running the latest iOS. It seems like the menu jumps into its fixed position too early, leading me to be ...

Routes for Express are throwing a 500 internal server error

My server is unable to locate the APIs that I have created in the API directory, which is resulting in a 500 internal server error. I have thoroughly checked routes.js and everything appears to be correct. Additionally, I have an error.js file for handlin ...

The error message "item is not defined in nodejs" indicates that the variable

I am facing an issue while trying to retrieve data from a JSON file using Node.js and Express. I have defined the methods with exports, but I keep getting errors in my browser. I am unsure why it is not functioning correctly, as I have specified the metho ...

I am experiencing difficulty with the function from flip.to not functioning properly on my Next.js project

I was given this script by flip.to <script> !function(b,e){ (b[e] = b[e] || []).push({ flipto: { bookingEngine: "Demo", companyCode: "XX", code: "YYYY", ...

There is no action taken by the ng-include directive

Attempting to integrate HTML content into my software's website using ng-include has proven to be a challenging task. To facilitate testing, I am currently working with simplified versions of my end objectives. Pertinent section of the project hierar ...

How can I toggle button states within a v-for loop based on input changes in Vue.js?

Within the starting point of my code: https://plnkr.co/LdbVJCuy3oojfyOa2MS7 I am aiming to allow the 'Press' button to be active on each row when there is a change in the input field. To achieve this, I made an addition to the code with: :dis ...

Not quite sure why the commitment is not being fulfilled

Before in my angular controller, I had: var getResponse = function () { $http.post("api/screen/", { Key: 'banana', value: null }) .then(onComplete, onError); }; var onError = function (reason) { $scope.error = "F ...

`amqplib - What is the current number of active consumers on the queue?`

Seeking insight on using the amqplib module for Node JS and RabbitMQ: 1) Is there a method to determine the number of subscribers on a queue? 2) What is the process for ensuring that each queue has only one consumer key? Appreciate any guidance! ...

Encountering an issue with retrieved items upon refreshing the webpage

My usual approach to fetching data from an external API involves the following steps: Using Fetch API: const [tshirts, setTshirts] = useState([]); const fetchData = () => { fetch('apiEndpoint') .then((response) => ...

How to quickly send data in AngularJS with only one button press

When I have a textarea and press the "Enter" button, data needs to be sent to the server. However, if I quickly press the "Enter" button several times, the "addNewCommentFactory.addComment" function sends multiple requests. Is there a way to send only one ...

Iterate over a deeply nested JSON array using Angular

My JSON structure looks like this: "bonds": [ { "name": "bond_0", "interface": [ "nic_0", "nic_1" ], "mode": "active_standby", "primary_interface": "nic_0" }, { "name": "bond_1", "interface": [ "nic_0", "nic_1" ], " ...

Need to send emails to two separate users? Look no further than Resend.com for a quick and

I am currently utilizing resend.com to send an email containing different variables to two distinct users. import type { NextApiRequest, NextApiResponse } from "next"; import { EmailTemplate } from "../../components/emails/EmailTemplate" ...

Unable to retrieve the path during an AJAX request in PHP and JavaScript

I'm struggling with passing data to the server-side using an ajax call. It's giving me an error saying 'The required path not found'. I am working with CodeIgniter for the MVC framework. Below is a snippet of the code: var url = "http: ...

Ways to achieve 8 columns in a single row using Javascript and Bootstrap

Recently, I created a simple function for searching movies and manipulating them in the DOM. The issue arises when a movie name is entered and the API response returns around 20-30 recommendations. I wanted to display this fetched data in 8 columns per row ...

Why is URL Hash Navigation not functioning when linking to a different page's slide on the carousel?

Why isn't the #tag link being recognized? Even though the data-hash items are visible in the URL window, the script doesn't seem to pick them up. The standard script used on the carousel page is as follows: $(document).ready(function() { $( ...

The Express API is failing to recognize the data keys that were sent from the React frontend, despite being clearly specified

I am facing an issue while trying to send data to a REST API using React hosted in a separate application. The API does not seem to receive the keys sent, even though I checked the results in Chrome and found this output:(2) ["imageSrc", File]0: "imageSrc" ...

Tips for implementing autocomplete functionality in AngularJS input fields

I attempted to integrate code from a website (http://jsfiddle.net/sebmade/swfjT/) into my program, but unfortunately, the output did not match what was expected. I am also looking to implement a search function by camera id. Can anyone provide assistance? ...

incorporating a timer into a JavaScript game

I am currently working on a memory card game where I want to include a stopwatch feature. I'm looking to display the time elapsed from when the user selects the first card until they win. However, I am struggling with keeping the stopwatch running smo ...