JavaScript the Fun Part


modern-javascript

promises


We know that function keyword in jS has many use cases; e.g.

and promises are function constructor which means it is a class that we got an instance of it to work with.

The first and only argument this constructor takes is a callback. So just one argument.

Then this callback itself takes two callbacks.

  1. first callback will be for resolving ...
  2. second callback will be for rejecting ...

New features

It is important to notice that promises are not a new feature to the core of the JavaScript Language. They are wrapper to make coding in JS much easier to solve problems we have with

const log = console.log.bind( console );
log( "--- start ---" );

setTimeout(function(){
    log( "This is a Macro task ..." );
}, 0);

function callback( fn1, fn2 ){
    log( "before fn1, fn2" );
    fn1( "This is for resolving ..." );
    log( "between fn1, fn2" );
    fn1( "This is for rejecting ..." );
    log( "after fn1, fn2" );
}

const p = new Promise( callback );
p.then( r => log( r ) ).catch( e => log( e ) );

log( "---- end ----" );

and the output

--- start ---
before fn1, fn2
between fn1, fn2
after fn1, fn2
---- end ----
This is for resolving ...
This is a Macro task ...

from the output we simply can understand that both

functions did not run as a main task. Also we have a setTimeout function which has less priority than main task.

And since we see the output of resolve() function, before setTimeout function; it means it has more priority than setTimeout but less than main task.

A kind of better setTimeout and main task.

Also we should notice that all three console.log are run immediately.

lets reverse the order of fn1, fn2

function callback( fn1, fn2 ){
    log( "before fn1, fn2" );
    fn2( "This is for rejecting ..." );
    
    log( "between fn1, fn2" );
    
    fn1( "This is for resolving ..." );
    log( "after fn1, fn2" );
}

the output

--- start ---
before fn1, fn2
between fn1, fn2
after fn1, fn2
---- end ----
This is for rejecting ...
This is a Macro task ...

from the output can see / say that since the fn2 is before fn1 it is run by promise as the result.

And this simply means both case are not happen. Either resolve() or reject() is happen. Which one comes first happens.

Also resolve() (= first fn) is delivered to then() and reject() (= second fn) is delivered to catch() block.

Core Idea

As you saw above delivering the result (either success or failure) by two callbacks which have less priority than main task is the essence of promises.

There are also things like

which is important but not as much as the above core concept.

Simple Example

Using this idea we are going to do a simple task in the back-end and bring the result back to us.

setInterval() has been used to simulate Main Task.

setTimeout() has been used to put the task for later execution.

Empty while loop has been used to simulate delay.

const log = console.log.bind( console );
log( "--- start ---" );

setInterval(function(){
    log( "simulating main task is running ..." );
}, 1000);

function backendStuff( index, resolve, reject ){
    setTimeout(function(){
        try{ 
            // throw "we have error in network!";
            while( ++index < 100000000 );
            resolve( "backendStuff done: " + index );
        } catch( ex ){
            reject( "backendStuff exception: " + ex );
        }
    }, 3000);
    // this is run as main task
    return index;
}

function callback( fn1, fn2 ){
    const r = backendStuff( 0, fn1, fn2 );
    // this is run as main task
    log( "start from:", r );
}

const p = new Promise( callback );
p.then( r => log( r ) ).catch( e => log( e ) );
log( p );
log( "---- end ----" );

And here is the output

--- start ---
start from: 0
Promise { <pending> }
---- end ----
simulating main task is running ...
simulating main task is running ...
backendStuff done: 100000000
simulating main task is running ...
simulating main task is running ...
simulating main task is running ...

And if we simulate and error like soo

throw "we have error in network!";

then output will be

--- start ---
start from: 0
Promise { <pending> }
---- end ----
simulating main task is running ...
simulating main task is running ...
simulating main task is running ...
backendStuff exception: we have error in network!
simulating main task is running ...
simulating main task is running ...
simulating main task is running ...

So without blocking the Main Tasks (= Main thread) we are able to do our tasks.

We will see more about promises in next posts.


Update: Sun Sep 29 2019 08:47:07 GMT+0330 (Iran Standard Time)