Promises

  1. Overview
    1. A synchronous function is a function that completes an operation before returning
    2. An asynchronous function starts an operation usually returns before the operation completes. The operation completes in the background, allowing other code to execute in the meantime
    3. Synchronous functions are for fast-performing code
    4. Time-intensive operations (Ex: downloading a file, performing slow calculations) should be performed by an asynchronous function to avoid locking the JavaScript engine in the browser
    5. Time-intensive operations can be implemented by Web Workers, which run on background threads (we won't cover Web Workers this semester)
    6. JavaScript commonly uses Promises to implement asychronous functions
    7. A Promise object is an object representing the eventual completion of the asynchronous operation
  2. Promise object
    1. A Promise object can be in one of three states:
      1. Pending - Asynchronous operation is still running
      2. Fulfilled - Asynchronous operation has completed successfully
      3. Rejected - Asynchronous operation has failed to produce intended result
    2. Once reaching the fulfilled or rejected state, the Promise object is settled (or resolved), and the state will not change again
    3. Promise constructor takes an executor function that executes in the background
      let promise = new Promise(function(resolve, reject) {
         // Execute immediately 
      });
      
      1. resolve parameter is a function to call when executor function has completed successfully (state becomes fulfilled)
        let promise = new Promise(function(resolve, reject) {
           // Simulate time-intensive operation; after 1 second indicate result is "Done"
           setTimeout(() => resolve("Done"), 1000);
        });
        
      2. reject parameter is a function to call when executor function has completed unsuccessfully (state becomes rejected)
        let promise = new Promise(function(resolve, reject) {
           // Simulate time-intensive operation; after 1 second indicate problem
           setTimeout(() => reject(new Error("Something went wrong")), 1000);
        });
        
    4. Notes about Promises
      1. A Promise can only produce a single result or an error
      2. reject() is usually called with an Error object
      3. resolve() or reject() may be called at any time, even immediately
      4. A Promise's state and result are internal and can't be accessed as object properties
  3. Consuming a Promise
    1. Promise.then() method
      1. Two function arguments:
        1. Result function that is called when Promise is fulfilled
        2. Error function that is called when Promise is rejected
      2. Example that executes result function
        let promise = new Promise(function(resolve, reject) {
           // Simulate time-intensive; after 1 second indicate result is "done"
           setTimeout(() => resolve("Done"), 1000);
        });
        
        promise.then(
           result => alert(result), // Shows "Done" after 1 second
           error => alert(error)    // Doesn't run
        );
        
      3. Example that executes error function
        let promise = new Promise(function(resolve, reject) {
           // Simulate time-intensive; after 1 second indicate result is "done"
           setTimeout(() => reject(new Error("Something went wrong")), 1000);
        });
        
        promise.then(
           result => alert(result), // Doesn't run
           error => alert(error)    // Shows "Error: Something went wrong" after 1 second
        );
        
    2. Promise.catch() method
      1. Used to provide a callback when Promise is rejected
        promise
           .then(result => alert(result))
           .catch(error => alert(error));
        
      2. Equivalent to then(null, errorFunc)
      3. If an exception is thrown in the fulfilled callback, the reject callback in catch() executes
        // Displays two alerts
        promise
           .then(result => {
              alert("In fulfilled callback");
              throw new Error("Something went wrong");		 
           })
           .catch(error => alert(error));
        
  4. Example function that returns a Promise that resolves to a boolean
    function isEven(num) {
       return new Promise(function(resolve, reject) {
          if (!Number.isInteger(num)) {
    	     setTimeout(() => reject(new Error("Argument is not an integer")), 1000);
    	  }
    	  
    	  if (num % 2 == 0) {
    		 setTimeout(() => resolve(true), 1000);
    	  }
    	  else {
    		 setTimeout(() => resolve(false), 1000);
    	  }	  
       });
    }
    
    isEven(2)
       .then(result => alert(result))
       .catch(error => alert(error));
       
    // Simplified 
    isEven(3).then(alert).catch(alert);
    
    // Catch executes 
    isEven(4.5).then(alert).catch(alert);
    
  5. async and await
    1. An alternative to using a Promise's then() method is to use await inside an async function
    2. An async function is a function that always returns a Promise
      async function test() {
         // Equivalent to return Promise.resolve("test");
         return "test";
      }
      
      // Displays "test"
      test().then(alert);
      
    3. The await operator waits for a Promise object to resolve and invoke the appropriate callback function before proceeding to the next statement
    4. Waiting for an asynchronous operation to complete pauses the current function's execution and resumes once the Promise resolves
    5. await can only be used in an async function
    6. Use try-catch to catch exception from a rejected promise
    7. Example async function
      async function tryNums() {
         const nums = [2, 3, 4, 6.5];
         for (let n of nums) {
            try {
               const result = await isEven(n);
      	     console.log(`${n} is ${result}`);
            }
      	  catch (ex) {
      	     console.log(`Error for ${n}: ${ex}`);
      	  }
         }
      }
      
      tryNums();
      
      2 is true
      3 is false
      4 is true 
      Error for 6.5: Error: Argument is not an integer