At what point does the promise's then function transition to being either fulfilled or rejected?

When dealing with promises in JavaScript, the then() method returns a promise that can be in one of three states: pending, fulfilled, or rejected. You can create a promise using the resolved and rejected methods to indicate when it should be fulfilled or rejected. However, sometimes using these methods within functions in the then() chain can be tricky, especially when working with normal asynchronous functions that have callbacks. Take the following example:

        User.findOne({
            where: {
                email: email
            }
        }).then(function(user){
            if(user){
                return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
            }else{
                var user = User.build({ email: email });
                console.log("ONE");
                User.generateHash(password, function(err, hash){
                    console.log("TWO");
                    if(err){
                        return done(err);
                    }else{
                        user.password = hash;
                        var newUser = user.save();
                        return newUser;
                    }
                 })
             }
          }, function(err){
            console.log("ERROR: ", err);
         }).then(function(newUser){
            console.log("THREE");
            return done(null, newUser);
          }, function(err){
             console.log("USER NOT CREATED: ", err);
           });

In this scenario, User.findOne() returns a promise that causes the console output to be ONE THREE TWO due to the asynchronous nature of the code execution. If you want to control when the second then statement gets executed, you may need to adjust your approach by properly handling the flow of promises in your code.

Although there are multiple ways to resolve this issue, understanding why and when the promise returned by the first statement becomes fulfilled is crucial for improving the overall performance and reliability of your code.

Answer №1

What actually occurs is that the function findUser is executed successfully (not rejected), so the first argument callback in the .then() method is triggered instead of the second.

Within the .then() block, you call console.log("ONE") and don't return anything because you end up in the else { block where a return statement is missing, resulting in an output of undefined.

Following this, the subsequent .then() method is invoked, leading to the execution of console.log("THREE").

At some later point in time, the callback from createHash is triggered, causing console.log("TWO") to be displayed.

A more appropriate approach would have been to structure User.generateHash such that it also returns a promise. This way, you could easily chain it with the rest of the promise sequence without having to manage asynchronous operations within the promise callback.

Answer №2

When initiating a promise, you can utilize the resolved and rejected methods to determine whether the promise should be fulfilled or rejected. However, it may not be apparent how to apply these methods within functions called in the then() method.

Upon employing the then method on a promise, the resolve/reject callbacks are not directly accessible - the then function handles this internally. Instead, it provides a new promise that will resolve with the return value of your callback. If the return value is a promise, it will be automatically processed (resolved or rejected) based on the outcome of that promise.

To address this scenario, it is essential to create a promise for the callback function within the then block, which can then be returned. Ideally, one can pre-convert the callback into a promise using promisify techniques, but alternatively, the Promise constructor can also be used (providing access to resolve/reject).

    User.findOne({
        where: {
            email: email
        }
    }).then(function(user){
        if (user){
            throw new Error('That email is already taken.');
        } else {
            var user = User.build({ email: email });
            console.log("ONE");
            var promise = new Promise(function(resolve, reject) {
//                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                User.generateHash(password, function(err, hash){
                if (err) reject(err);
                else     resolve(hash);
            });
            return promise.then(function(hash) {
                console.log("TWO");
                user.password = hash;
                var newUser = user.save();
                return newUser;
            });
        }
    }).then(function(newUser){
        console.log("THREE");
        return done(null, newUser);
    }, function(err){
        console.log("ERROR: ", err);
        return done(null, false, req.flash('signupMessage', err.message));
    });

Answer №3

Assuming that done() and User.generateHash() do not return promises, the 2nd level of this program can proceed without delay to the next then() block.

In order to address this issue or to ensure that your print statements display ONE TWO THREE, consider making the following adjustments:

User.findOne({
    where: {
        email: email
    }
}).then(function(user){
    var promise;
    if(user){
        promise = done(null, false, req.flash('signupMessage', 'That email is already taken.'));
    }else{
        var user = User.build({ email: email });
        console.log("ONE");
        promise = User.generateHash(password, function(err, hash){
            return new Promise(function(resolve, reject){
                console.log("TWO");
                if(err){
                    reject(done(err));
                }else{
                    user.password = hash;
                    var newUser = user.save();
                    resolve(newUser);
                }
            });
        });
    }

    return promise;
}, function(err){
    console.log("ERROR: ", err);
}).then(function(newUser){
    console.log("THREE");
    return done(null, newUser);
}, function(err){
    console.log("USER NOT CREATED: ", err);
});

In this manner, the initial then block will pause until the promise is fulfilled before proceeding to the subsequent then block. This ensures that User.generateHash must resolve before advancing to the next then block as the variable promise relies on it.

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

Different approach for searching and modifying nested arrays within objects

Here is an example of the object I am working with: { userId: 111, notes: [ { name: 'Collection1', categories: [ { name: 'Category1', notes: [ {data: 'This is the first note& ...

"Create a new row in the list by selecting an option from the drop-down

I'm experimenting with the following scenario. There is a function that reveals a hidden list based on a dropdown selection. To see it in action, please click here. What I am aiming to achieve is for Option1 to display the content of #List-Option1 ...

Unable to utilize a function within a mongoose schema

I encountered an issue while attempting to access the schema methods of a mongoose schema in TypeScript. Schema const userSchema: Schema<IUser> = new Schema( { name: { type: String, required: [true, "Name is required"], ...

The Node gracefully disconnects and apologizes: "I'm sorry, but I can't set headers after they have already

events.js:160 throw er; // Unhandled 'error' event ^ Error: Can't set headers after they are sent. register.js:20:18 register.js user.save(function(err) { if(err){ return mainFunctions.sendError(res, req, err, 500, ...

Using jQuery to toggle the visibility of table data cells across various tables on a single webpage

On my webpage, I have multiple tables and I'm trying to add more rows or close table data cells using jQuery. However, I seem to be encountering an issue as it's not working properly. <table class="table" ><tr> < ...

AngularJS: resolving route dependencies

I have a variable $scope.question that contains all the questions for the page. My goal is to loop through the questions page by page. To achieve this, I created a function called questionsCtrl and I am calling this function in the config while setting up ...

What is the best way to include the existing query string value in the hyperlinks on my page?

My main coding objective is to simultaneously search multiple websites. To accomplish this, I want to create a query string containing the search terms (primarily for analytics purposes) using a form. By utilizing JavaScript, I am aiming to include the s ...

Issue: React build script does not support conversion from 'BigInt' to 'number' error

After developing the UI using create-react-app, I encountered an issue. The UI works fine with the normal npm start command, but when I try to build it with npm run build, I get an error saying 'Conversion from 'BigInt' to 'number' ...

Can you include the dollar symbol in the currency format?

I currently have this code that formats numbers into currency format, however I would like to include the dollar sign ($) before the numbers. document.getElementById("numbers").onblur = function (){ this.value = parseFloat(this.value.r ...

Navigating through Angular JS validation procedures step by step

I have a wizard in angular js consisting of multiple steps: <wizard on-before-step-change="log(event)" on-step-changing="log(event)" on-after-step-change="log(event)" user="user"> <step title="step 1"> </step> <step title="step 2"& ...

Retrieve JSON data and use a button to sort and display markers on a Google Map

Is it possible to modify my function in order to use different PHP files with separate buttons without duplicating the code? Currently, I have a function that displays markers on an external HTML page when a button is clicked. The data is retrieved from fi ...

Eliminate web address parameter using regular expressions

Looking to remove a specific URL parameter from a given URL. For instance, if the URL is: http://example.com?foo=bar&baz=boo And I want to eliminate foo=bar to get: http://example.com?baz=boo Or removing baz=boo would leave me with: http://exampl ...

Understanding the readability of JavaScript arrays.ORDeciphering

Recently, I've been working with a JSON OBJECT that looks something like this { "kay1": "value1", "key2": "value2", "key3":{ "key31": "value31", "key32": "value32", "key33": "value33" } } However, I am interested in converting it ...

Transferring Information Between Vue Components

In my project, I have implemented 3 Vue components to work together seamlessly. Component A is responsible for listening to an event triggered by a user clicking on HTML text and storing the data into a variable. Component B utilizes this data to make an A ...

How to identify the character encoding in a node.js request

Did you know that Facebook chat has a feature where it automatically detects and displays messages in a left-to-right format when typing in English, but switches to right-to-left style when adding right-to-left characters? I'm curious about how Faceb ...

Web Page Content Scrambling/Character Exchange

I've encountered a perplexing issue that seems to strike randomly, yet I've managed to replicate the problem on three different desktops. Oddly enough, some other desktops never experience this issue and I'm at a loss as to what could be cau ...

A step-by-step guide on executing a callback function once the animation has finished with frame-motion

I'm facing an issue with my component called AnimatedText. After the animation is complete, I want the words filled in the underlineLines prop to be underlined. How can I achieve this? I attempted using the onAnimationEnd function, but it didn't ...

Can one extract a property from an object and assign it to a property on the constructor?

Currently working with TypeScript, I am looking to destructure properties from an object. The challenge lies in the fact that I need to assign it to a property on the constructor of the class: var someData = [{title: 'some title', desc: 'so ...

qunit timer reset

I have developed a user interface for manually launching qunit tests. However, I have noticed that the qunit test timer starts when displaying the interface, rather than when starting the actual test. For example: var myFunction = function (){ test ...

The drop-down menu fails to appear when I move my cursor over it

#menu { overflow: hidden; background: #202020; } #menu ul { margin: 0px 0px 0px 0px; padding: 0px 0px; list-style: none; line-height: normal; text-align: center; } #menu li { display: inline-block; } #menu a { display: block; position: relative; padding ...