JavaScript Promise Fundamentals

While I am quite familiar with coding in JavaScript, the benefits of promises in the JS world still seem somewhat unclear to me. Below is an example of asynchronous calls using callbacks nested within each other.

(function doWorkOldSchool() {

    setTimeout(function() {

        // When the work is done, resolve the promise

        console.log("work done");

        setTimeout(function goHome() {

            // Once home, resolve the promise

            console.log("got home");

            try {

                setTimeout(function cookDinner() {

                    // This exception will not be caught

                    throw "No ingredients for dinner!";
                    console.log("dinner cooked");

                    setTimeout(function goToSleep() {

                        // Once asleep, resolve the promise

                        console.log("go to sleep");

                    }, 2000);

                }, 2000);

            } catch (ex) {
                console.log(ex);
            }

        }, 2000);

    }, 2000);

}());

Two main issues arise with this code:

  1. Exceptions thrown inside callbacks are difficult to handle. The exceptions are out of scope and cannot be properly dealt with as they propagate upwards. How can such exceptions be managed?

  2. The nesting structure could lead to deeply nested code that might become unmanageable even if the callback functions are kept separate from the setTimeout code.

Can someone clarify if there are any other significant problems or advantages to this type of coding?

Now, I have restructured the program to accomplish the same tasks using promises:

function doWork() {

    return new Promise(function(res, rej) {

        // perform asynchronous tasks

        setTimeout(function() {

            // When the task is complete, resolve the promise

            res("work done");

        }, 2000);

    });
}


function goHome(succ) {

    console.log(succ);

    return new Promise(function(res, rej) {

        // perform asynchronous tasks

        setTimeout(function() {

            // Once done, resolve the promise

            res("got home");

        }, 2000);

    });
}

// Other functions like cookDinner and goToSleep follow a similar pattern...


doWork()
    .then(goHome)
    .then(cookDinner)
    .then(goToSleep)
    .then(function(succ) {

        console.log(succ);

    }, function(err) {
        console.log(err);
    });

Compared to the previous solution, this approach seems to eliminate certain drawbacks:

  1. Exceptions thrown inside handlers can now be caught by the error handler further down the chain.

  2. Rejected promises can also be handled by the chained error handler.

  3. The code appears much cleaner and more organized.

Does my understanding align with these advantages, or are there other pros and cons to consider between these two approaches?

Answer №1

Your current code structure is utilizing some patterns that are considered anti-patterns in programming practice. It's not recommended to create promises using methods like new Promise or "deferreds" directly in your application code. Mixing callbacks with promises can lead to losing exception bubbling, which defeats the purpose of using promises and can make your code unnecessarily verbose.

Instead, you can opt for using a library or implementing a delay function yourself:

function delay(ms, val) {
     return new Promise(function(resolve){setTimeout(resolve.bind(null, val), ms);});
}

With this implementation, you can refactor your code as follows:

function doWork() {
    return delay(2000, "work done");
}

doWork()
    .then(function(success) {
        console.log(success);
        return delay(2000, "got home");
    })
    .then(function(success) {
        console.log(success);
        throw new Error("No ingredients for dinner Exception!"); // Avoid throwing strings, it's another anti-pattern
        // No need to add code after throw statement
    })
    .then(function(success) {
        return delay(2000, "zzz.. zz..");
    })
    .catch(function(error) {   // Use .catch instead of the second argument in .then for error handling
        console.log("error: ", error);
    });

Using .catch instead of the second argument in .then is important for proper error handling. To better understand, compare it to how you would write the synchronous version:

try {
    doWork();
    console.log("work done");
    sleep(2000);
    console.log("got home");
    sleep(2000);
    throw new Error("No ingredients for dinner Exception!";)
    sleep(2000);
    console.log("zzz.. zz..");
}
catch(e) {
    console.log("error: ", err);
}

Do you see the difference now?

Answer №2

When working with Promises, it's important to remember that they can only return or decide on a value once. Once a value is determined, it cannot be changed. For example, if a user clicks on a div element, the Promise will execute only once.

p = new Promise(function (res, rej) { 
var b = document.getElementById('helloWorld'); 
  b.onclick = function() {
    res(prompt('value'));
  }; 
});
p.then(function(val) { console.log(val); });

Only one value will ever be logged in this scenario. This feature of Promises is particularly helpful for GUI and game/application controls. Additionally, having multiple event listeners inside a Promise can be useful for tasks like loading images and files. With Promises, you can create separate success and failure handlers and still have them react properly. Unlike traditional event handlers, Promises won't generate errors if the failure handler is created after the success handler; instead, they wait until both functions are ready to call them. This allows you to focus on how to respond to events without worrying about timing issues. It's a great tool for handling loading processes effectively.
Check out this awesome guide to learn more about Promises!

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

Swapping out ChildNode data for HTML elements

When attempting to replace a node value with HTML content, I encountered an issue where the HTML tags were being displayed on the HTML page instead of applying the intended style. <style> .YellowSelection { background: yellow; } < ...

Having trouble sending the information to Parse.com using the website

I am a beginner with the Parse database and I am currently working on implementing a code that allows users to sign up, with their information stored in the Parse database. However, I am encountering an issue where the values are not uploading as expected. ...

Tips for Implementing a Footer Component in ReactJS

My current setup includes: https://i.stack.imgur.com/2gvHk.png A navigation bar Nested content with a wizard and partial windows. A dynamic footer whose buttons and functionality need to change based on certain scenarios in the content. Currently, I ha ...

What could be causing a parse error and missing authorization token in an AJAX request?

I recently wrote some code to connect a chat bot to Viber using the REST API. The main part of the code looks like this -: $.ajax({ url : url , dataType : "jsonp", type : 'POST', jsonpCallback: 'fn', headers: { 'X-Viber-Auth- ...

What methods can be used to initiate form error handling in React/Javascript?

I am currently utilizing Material-UI Google Autocomplete, which can be found at https://material-ui.com/components/autocomplete/#google-maps-place, in order to prompt users to provide their address. https://i.stack.imgur.com/nsBpE.png My primary inquiry ...

Passing a leading zero function as an argument in JavaScript

Is there a method for JavaScript to interpret leading zeros as arguments (with the primitive value as number)? I currently have this code: let noLeadingZero = (number) => { return number } console.log('noLeadingZero(00):', noLeadin ...

Perform calculations for product and sum when button is clicked

I received the following task for an assignment: Develop 3 functions along with a form. The first function should execute upon button press and utilize the other 2 functions. These two functions are supposed to compute the sum of two numbers and the ...

What is the process for generating an attribute in a system?

If I want to create an element using plain JavaScript, here is how I can do it: var btn = document.createElement('button'); btn.setAttribute('onClick', 'console.log(\'click\')'); document.body.appendChild( ...

What are some best practices for enhancing the elegance of this JS code?

Within my React app that utilizes Material UI components, I created a code snippet to handle the sizing of contact form fields. These fields are dynamic elements that can be added or removed based on certain configurations. Although the code I implemented ...

Is it permissible for me to incorporate a package from the dependencies listed in the package-lock.json file into my

I'm looking to incorporate date-fns into my project. I currently have react-datepicker, which also uses date-fns. Can I simply utilize date-fns from react-datepicker, or do I need to install it separately in my project? ...

Aurelia's navigation feature adds "?id=5" to the URL instead of "/5"

I have set up my Aurelia Router in app.ts using the configureRouter function like this: configureRouter(config, router: Router) { config.map([ { route: ['users', 'users/:userId?'], na ...

Can I add the object to the DOM and hold off on executing any events until the next

Just came across this intriguing interview question (shown below). Could someone please provide an explanation of the question and then offer a solution? Develop a function that takes an object and adds it to the DOM, ensuring that events are stored unt ...

Every time Jquery tries to retrieve cookies, it consistently returns as undefined

Having trouble accessing Application cookies using jquery in my Asp.Net MVC App. Check out this Screenshot of Cookie and its Value. I've been trying to access the Cookie with $.cookie('ASP.NET_SessionId'); but it keeps returning "undefined" ...

Retrieve container for storing documents in JavaServer Pages

Previously, I created JSP and HTML code to upload a file from the hard disk into a database using <input type="file" name="upfile"> However, a dialog box with an "Open" button is displayed. What I am looking for is a "Save" button that will allow u ...

Utilizing ReactJs refs to set focus on an input element

Exploring the use of refs in ReactJs to focus a textbox from a button click. Encountering the following error message: bundle.js:114 Uncaught TypeError: Cannot read property 'focus' of undefined Check out the Source Code below: class FocusTex ...

What strategy should be employed to ensure that Javascript is operational upon completion of page loading?

I'm facing a challenge with running my JavaScript on my ASP.NET webform page. It seems like the elements are not fully loaded when the script runs, which could be causing issues. How can I use jQuery to ensure that my script is placed at the bottom of ...

Step-by-Step Guide: Crafting a Non-Ajax Popup Chat Application

Recently, I created a unique dating website with a one-to-one chat feature similar to Facebook. However, I implemented this using the ajax technique and the setInterval function in JavaScript for regular updates. Upon reflection, I believe that this appr ...

Explore the possibilities of using a unique custom theme with next.js, less, and ant design

Trying to customize the default theme in antdesign has been a challenge for me. I've switched from sass to less, but there seems to be something that just won't work. I've exhaustively searched online for solutions - from official nextjs ex ...

Looping through multiple table rows with AngularJS ng-repeat

I am currently working on making the code below functional, which involves data being retrieved from an API: ... <tbody ng-repeat="verbinding in data.connection" style="border-bottom:2px solid black;"> <tr> <td></td> ...

What is the process for converting a file to base64 encoding, and then uploading it as a multipart file to a backend API using JavaScript

I am working on a project to create a micro-service that can receive base64 encoded data. However, when attempting to send more than 6 MB of data, I encountered the following error message: The multi-part request contains parameterized data (excluding ...