Using Promises

×

About Promise

How Promises Work

A promise is an object which can be returned synchronously from an asynchronous function.
It will be in one of 3 possible states:

  • Fulfilled: onFulfilled() will be called (e.g., resolve() was called)
  • Rejected: onRejected() will be called (e.g., reject() was called)
  • Pending: not yet fulfilled or rejected

Promises following the spec must follow a specific set of rules:

  • A promise or “thenable” is an object that supplies a standard-compliant .then() method.
  • A pending promise may transition into a fulfilled or rejected state.
  • A fulfilled or rejected promise is settled, and must not transition into any other state.
  • Once a promise is settled, it must have a value (which may be undefined). That value must not change.
  • A Promise can be resolved or rejected, exclusively and only once.
  • A call to promise.then returns a promise, so that we can call the next promise
  • A value returned by a .then handler is immediately passed to the next handler
  • If the returned value is a promise, then the further execution is suspended until it settles.
  • A fetch() promise will reject with a TypeError when a network error is encountered or CORS is misconfigured on the server side
  • By default, fetch won’t send or receive any cookies from the server

Chained Promises

Chained Promises Sample

function testMe(myUrl, test, dataError) {
    dumpMessage("Testing Promise Chaining: "  + test, false);
    let url = myUrl;
    fetch(url)                           // fetch returns a promise       
        .then(response => {              // Our Success Handler returns response Object 
            if(response.ok) {
                if ( dataError) {        // Just for testing - Mimicking a Data Proessing Error
                    throw new Error('Data Processing Error');
                }  
                return response.json();
            }
            throw new Error('Network response was not ok  at Chain Level 1');
        })  // Catches Exception for our Success Handler and for our Reject Handler 
    .catch(e => { 
        dumpMessage("Exception at Chain Level 1  : " + e,true );  
        throw new Error('Initial Fetch failed for URL: ' + url); 
    })
    .then(jsonData => { dumpMessage("ID: " + jsonData.id + " - name : " + jsonData.name,false ); return(jsonData.name);} )
    .then( (user) => {  dumpMessage("Username: " + user, false);  return fetch("https://api.github.com/users/"+user ); })
        // Load the response as json
    .then(response => response.json())     
        // Show the avatar image (githubUser.avatar_url) for 3 seconds (maybe animate it)
    .then(githubUser => {
        dumpMessage("Displaying Github Avatar Image for 3 seconds!", false);  
        let img = document.createElement('img');
        img.src = githubUser.avatar_url;
        img.className = "promise-avatar-example";
        document.body.append(img);

        setTimeout(() => {
            img.remove();
            dumpMessage("Removed Github Avatar Image !", false);
        }, 3000); // (*)
    })
    .catch(err => {             // Catch all Hanlder return true/false for retry logic 
        dumpMessage("CatchAll Error-handler: " + err, true);
        });
} 

Promises and Error Handling

Overview

Pomisess Error Handling Diagram
Image promise_img1.jpg NOT Found“;[/insert_php]

Handle Errors – Pattern 1


save().then(
  handleSuccess,
  handleError
);


  • Problem Exceptions in handleSuccess() are not catched

Handle Errors – Pattern 2


save()
  .then(handleSuccess)
  .catch(handleError)


Sample:
 fetch(url)                           // fetch returns a promise       
        .then(response => {              // Our Success Handler returns response Object 
            if(response.ok) {
                if ( dataError) {        // Just for testing - Mimicking a Data Proessing Error
                    throw new Error('Data Processing Error');
                }  
                return response.json();
            }
            throw new Error('Network response was not ok  at Chain Level 1');
        })  // Catches Exception for our Success Handler and for our Reject Handler 
    .catch(e => { 
        dumpMessage("Exception at Chain Level 1  : " + e,true );  
        throw new Error('Initial Fetch failed for URL: ' + url); 
    })
    .then(jsonData => { dumpMessage("ID: " + jsonData.id + " - name : " + jsonData.name,false ); return(jsonData.name);} )
    ...

Handle Errors – Pattern 3


save()
  .then(
    handleSuccess,
    handleNetworkError    -> Handle and Fix Network Error
  )
  .catch(handleProgrammerError) -> Handle anf Fix ProgamError in handleSucessFunction


Sample:
    fetch(url)                      
        .then(response => {              // Our Success Handler 
            if(response.ok) {
                if ( dataError) {        // Mimicking a Data Proessing Error
                    throw new Error('Data Processing Error');
                }  
                return response.json();
            }
            throw new Error('Network response was not ok.');
        },      
        err => {    // Now we handle the error (rejection reason)
            dumpMessage("Handle error (rejection reason): " + err,true );  
               // Create a Dummy object
            dumpMessage("Create a Default Object to Finish Promise Chain");    
            myObj = { "name":"hhutzler", "id":999 };
            return myObj;
        } )
        .catch(e => { dumpMessage("Processing Error Chaining at Level 1  : " + e,true );  
            dumpMessage("Create a Default Object to Finish Promise Chain");    
            myObj = { "name":"hhutzler", "id":999 };
            return myObj;                   
        })
            //        ,        // Now we handle the error (rejection reason)
            //e => { dumpMessage("Initial Fetch Error: " + e,true );  throw new Error('Initial Fetch Error for URL' + url); } )
            //.then(response =>  response.json() )
    .then(jsonData => { dumpMessage("ID: " + jsonData.id + " - name : " + jsonData.name,false ); return(jsonData.name);} )
    ....

Reference

CORS Details

Setup

  • Html Page was loaded from: localhost
  • PHP file was loaded from: http://hhutzler.de

JavaScript Code

console.log("Testing Promise Chaining by Returning New Promises");
fetch("http://hhutzler.de/myID.php")
  .then(response =>  response.json() )
  .then(jsonData => { console.log("ID returned by node-fetch Module : " + jsonData.id + " - name : " + jsonData.name );} );

PHP Code


$myObj = new stdClass();
$myObj->id=999;
$myObj->name = "hhutzler";
$myObj->city = "Sattelmanmsburg";

$myJSON = json_encode($myObj);

echo $myJSON;

Expected CORS Error

 
Failed to load http://hhutzler.de/myID_NW.php: No 'Access-Control-Allow-Origin' header is present on the requested resource. 
Origin 'http://localhost' is therefore not allowed access. 
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

FIX – Add Access-Control-Allow-Origin to the PHP File

 
header('Access-Control-Allow-Origin: *'); 

$myObj = new stdClass();
$myObj->id=999;
$myObj->name = "hhutzler";
$myObj->city = "Sattelmanmsburg";

$myJSON = json_encode($myObj);

echo $myJSON;

Running my first Node.js project with Netbeans

×

Node.js Overview

Node.js processing model

  • Follows single threaded even loop processing model which can handle more and more concurrent requests easily
  • Another important point to keep in mind that Node.js architecture has two types of threads

Main Thread (Single Thread)

  • Called as internal thread as well, all the non I/O blocking requests are assigned using Main thread

Worker Thread(s)

  • Used for assigning blocking I/O heavy requests

Here are the details on how the single threaded event loop based processing works

  • Client sends the request to web sever
  • Node.js web server receives client requests from different clients, puts them into a queue. This queue is called event queue
  • Node.js has an internal component called event loop which runs the loop on event queue, assigns the request to a single thread if the request is non-blocking
  • In case of blocking (I/O heavy) request, event loop will assign this to worker thread
  • While worker thread is processing the request, event loop will continue to process other requests available in event queue

Node.js and Standard Webserver Flow-Diagramms

Node.js Flow Diagram
Image nodejs_img1.jpg NOT Found“;[/insert_php]
Standard Webserver Flow Diagram
Image nodejs_img2.jpg NOT Found“;[/insert_php]

My First Node.js Web Application

How it works ?

  • Netbeans runs our Code in 2 Steps
  • Step1: Netbeans start a nodejs server process to execute HTTP server Code
  • –> Our HTTP server is now listening on port : 9900
  • Step2: Netbeans starts a Chrome Browser Windows and connects to Port 9900
  • –> Our Browser display the Content provided by our Http Server

Run a first Node.js Project

Setup a Node.js project properties within Netbeans

Step 1 : Configure Netbeans Project Run Methode Step 2 : Configure Node.js Properties
Image nodejs_img1.jpg NOT Found“;[/insert_php] Image nodejs_img2.jpg NOT Found“;[/insert_php]

Add the following Code to main.js

const http = require('http');
const port = 9900;

const requestHandler = (request, response) => {
    let funcName = "requestHandler";
    if (request.method === 'GET' && request.url === '/' ){
        dumpMessage(funcName, "Request Method: GET - request URL: " + request.url);
        response.writeHead(200, {"Content-Type": "text/html"});
        response.end('Hello Node.js Server - at:  ' + getTime() + '!');
    }
};

const server = http.createServer(requestHandler);

server.listen(port, (err) => {
    let funcName = "server.listen";
    if (err) {
        return console.log(funcName, 'something bad happened: ' + err);
    }

  dumpMessage(funcName, 'server is listening on port: ' + port);
});

function dumpMessage(testName, mesg) {                                       
    console.log(getTime() + testName + ":: --> " + mesg);           
};
    
function getTime() {
    var ts = new Date();
    return ts.toLocaleTimeString()+ ":" + ts.getMilliseconds() + "  ";
};

Finally run the Node.js Code

Step 3 : Run the project
Image nodejs_img3.jpg NOT Found“;[/insert_php]

Run above Node.js project manaully

Start the http Server

D:\xampp\htdocs\pvdata\testNodejs> "D:\Program Files\nodejs\node.exe"  "D:\xampp\htdocs\pvdata\testNodejs\main.js"
17:14:42:536  server.listen:: --> server is listening on port: 9900

Redirect your browser to the Listening Adress of our Web Server: http://localhost:9900

Browser Output
Image nodejs_img4.jpg NOT Found“;[/insert_php]

Debugging a Node.js Application with Netbeans – Currently NOT working due to Netbeans Bug 271238

"D:\Program Files\nodejs\node.exe" "--debug-brk=9292" "D:\xampp\htdocs\pvdata\testNodejs\main.js"
 (node:10116) [DEP0062] DeprecationWarning: `node --debug` and `node --debug-brk` are invalid. 
Please use `node --inspect` or `node --inspect-brk` instead. 

Done.

Debugging ourNode.js Application manually

Start the node.js process with inspect-brk option

  • Enable inspector agent
  • Listen on default address and port (127.0.0.1:9229)
  • Break before user code starts
D:\xampp\htdocs\pvdata\testNodejs> node --inspect-brk main.js
Debugger listening on ws://127.0.0.1:9229/9391ed9f-a46d-4f49-bee2-b83d6dafb055
For help see https://nodejs.org/en/docs/inspector

Attach Chrome Devtools 55+ to the Node.js Debugger process

  • Open a Chrome DevTool 55+ Browser session with following URL: chrome://inspect
  • Select : Open dedicated DevTools for Node
Attach Chrome DevTools to Node.js Inspector
Image nodejs_img50.jpg NOT Found“;[/insert_php]

CLI should respond with: Debugger attached

D:\xampp\htdocs\pvdata\testNodejs> node --inspect-brk main.js
Debugger listening on ws://127.0.0.1:9229/9391ed9f-a46d-4f49-bee2-b83d6dafb055
For help see https://nodejs.org/en/docs/inspector
Debugger attached.

Start Debugging

Debug Node.js by viewing Sources and setting Breakpoint
Image nodejs_img51.jpg NOT Found“;[/insert_php]

Reference