JavaScript Promises

Taming Async flow

Motivation

Solution to "callback hell"

Common Promise example

Basics

Use

Create

Flow

Promises

Basics

Basics

What are promises?

Promises are an alternative way to manage your  asynchronous code in JavaScript.

Basics

What are promises?

a promise is an object who represents the result of an asynchronous operation.

It is a place holder for a value that will become available in the future.

Implementation status

Basics

Spec 

Standard Libraries

Full list

Basics

Promises Axiomas

  • Will always be Async
    Executed at least on the next tick / frame

  • a Promise handler always return another Promise

  • Promises can be used only once

Basics

Promises states

  • PENDING    - The initial state of a promise.

  • RESOLVED  - a successful operation - returns a Value.

  • REJECTED  - a failed operation - throws an exception.
                            has a 'Reason' property.

  • (SETTLED)   - means either resolved or rejected
                                        Once a promise is settled, it is immutable
                                        and cannot be changed.

Basics

Using

Promises

Promise.prototype.then( )

Use

  
    get_user(id)
	 .then(on_resolved, on_rejected)
  
   get_user(862726)
	.then(user_data =>  console.log(user_data) //success...
             ,err =>  console.error(err.message)) //failure... 
           

Chaining Promises

Use

"a Promise handler always return another Promise"

therefore it can be chained to other Promises

  
    get_user(id)
	 .then(on_resolved_1, on_rejected_1)
         .then(on_resolved_2)
         .then(null, on_rejected_2)

Promise.prototype.catch( )

Use

  
    get_user(id)
	 .then(null, on_rejected)
     
  
    get_user(id)
	 .catch(on_rejected)

Promise.prototype.catch( )

Use

    
     get_user(id)
        .then(step1)
        .then(step2)
        .then(step3)
        .then(step4)
        .catch( err => {
            // The chain is broken and goes to the catch
            // Handle any errors from all above steps
        })

Chaining Promises( )

Promise.prototype.catch( )

Use

    
     get_user(id)
        .then(step1,on_get_user_reject)
        .then(step2,on_step1_reject)
        .then(step3,on_step2_reject)
        .then(step4,on_step3_reject)
        .catch( err => {
            // The chain is NOT broken
            // Handle any errors from step4
        })

Chaining Promises( )

Creating

Promises


function get_user(id){

      return new Promise((resolve, reject) => {

        fetch(`https://somedomain.com/api/users/${id}`)
            .then(response => response.json())
            .then(user_data => {
            	if(user_data.active) resolve(user_data);
                else reject(new Error('This user is not active any longer'))
                //reject due to an application logical error
            })
            .catch(err => reject(err)) //reject due to a communication error
      })

}

Create

new Promise( )

const fs = require('fs');

function readFile(filename, enc){

      return new Promise((resolve, reject) => {

         fs.readFile(filename, enc, (err, data) => {
           if (err) reject(err);
           else resolve(data);
         })
        
      })
}

Create

Promisification

const fs = require('fs');

const read_file = Promise.promisify(fs.readFile);
    
read_file("./path/to/myfile.js", "utf8")
    .then( contents => console.log(`The content of myfile.js is: ${contents}`) )
    .catch( err => console.log(`Error reading myfile.js ${err.message}` ));

Promise.promisify()  

Create

const util = require('util');
const fs = require('fs');

const stat = util.promisify(fs.stat);

stat('./some/path')
  .then( stats => {
    // Do something with `stats`
  }).catch( error => {
    // Handle the error.
  });

util.promisify()  

Create

    
    const fs = Promise.promisifyAll(require("fs"));

    fs.readFileAsync("myfile.js", "utf8")
      .then( contents => console.log(contents))
      .catch(err => console.error(err.message) );

Promise.promisifyAll()

Create

Controll Async Code Flow

    
    fetch('/api/friends')
        .then(step1)
        .then(step2)
        .then(step3)
        .then(step4)
        .catch( err => {
            // Handle any error from all above steps 
        })

Flow

Chaining Promises( )

    
    var items = [0,1,2,3,4,5,6,7,8,9];

    Promise.each(items, item => {
    
      return Promise.delay(Math.random() * 2000)
                  .then(()=>  console.log(`Finished item: ${item}`) );
    
    })
    .then( origin_array => {
        console.log(`All tasks are done now... ${origin_array}`);
    })
    .catch( err => 
        console.error(err.message)
    });

Promise.each()

Serial iteration on a collection
and then...

Flow

    
    var items = [0,1,2,3,4,5,6,7,8,9];

    Promise.map(items, item => {
        
        return Promise.delay(Math.random() * 2000)
            .then(()=> {
              console.log(`Finished item: ${item}`);
            });
    
    }, {concurrency: 3}) //adjust concurrency...
    
    .then((resultValuesArray) => console.log('All tasks are done now...'))

    .catch( err => console.error(err.message));

Promise.map()

Parallel iteration on a collection
and then...

Flow

    
    Promise.all(pending_promises_Array)
        .then( items => console.log('All operations are complete!', 'items: ',items))
        .catch( err =>  console.error( err.message ) );

Promise.all()

Parallel execution of different async operations
and then...

  • input - an array of promises
  • output an array of results values
    (ordered respectively to the original array order!)

Flow

    
    Promise.props(pending_promises_obj)
        .then( results => {
             console.log('All operations are complete!, result: ');
             console.dir(results);
        })
        .catch( err => {
             console.error(err.message);
        })

Promise.props()

Parallel execution of different async operations
and then...

  • input - an Object with promises key/value pairs
  • output - an Object of results key/values
    (easier to work with results...)

Flow

    
    Promise.race(pending_promises_Array)
        .then( item => {
            console.log(`The first operation is complete!, first item: ${item}`);
        })
        .catch( err => {
             console.log(`Error: ${err.message}`);
        })

Promise.race()

The .then( ) handler runs after the
first async operation is complete

Flow

    
    Promise.some(pending_promises_Array, 2)
        .spread( (first, second) => {
            console.log(`first: ${first}, second: ${second}`) 
        })      
        .catch( err => {
            console.log(`Error: ${err.message}`) 
        })

Promise.some()

Similar to race()

The .then() handler runs after the (n)th async operation is complete

Flow

Basics

Use

Create

Flow