JavaScript Promises

@tejesh95

Tejesh P

Synchronous Code

Synchronous Requests block the execution of code which creates "freezing" on the screen and an unresponsive user experience.

But I know how to use callbacks ...

Nested code impacts readability in negative way

Nested code impacts readability in negative way

CallBack Hell

Escape from callback hell...? 

Stay away from anonymous functions

var form = document.querySelector('form')
form.onsubmit = function (submitEvent) {
  var name = document.querySelector('input').value
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, function (err, response, body) {
    var statusMessage = document.querySelector('.status')
    if (err) return statusMessage.value = err
    statusMessage.value = body
  })
}
var form = document.querySelector('form')
form.onsubmit = function formSubmit (submitEvent) {
  var name = document.querySelector('input').value
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, function postResponse (err, response, body) {
    var statusMessage = document.querySelector('.status')
    if (err) return statusMessage.value = err
    statusMessage.value = body
  })
}

Stay away from anonymous functions

Stay away from anonymous functions

document.querySelector('form').onsubmit = formSubmit

function formSubmit (submitEvent) {
  var name = document.querySelector('input').value
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, postResponse)
}

function postResponse (err, response, body) {
  var statusMessage = document.querySelector('.status')
  if (err) return statusMessage.value = err
  statusMessage.value = body
}

Promises

Promises are a way to write async code that still appears as though it is executing in a top-down way,

and

handles more types of errors due to encouraged use of try/catch style error handling.

Promises

Promises - Can be created from constants 

var value = 10;
var promiseForValue = Promise.resolve(value);
// equivalent to
var promiseForValue = new Promise(function (fulfill) {
  fulfill(value);
});

JQuery $.ajax is a promise

var jQueryPromise = $.ajax('/ajax-endpoint');
var realPromise = Promise.resolve(jQueryPromise);
// equivalent to
var realPromise = new Promise(function (fulfill, reject) {
  jQueryPromise.then(fulfill, reject);
});

Rejected Promises creation

var rejectedPromise = Promise.reject(new Error('Whatever'));
// equivalent to
var rejectedPromise = new Promise(function (fulfill, reject) {
  reject(new Error('Whatever'));
});

But I know how to use callbacks ...

can still be optimized further!

Promise.all( )

Promise.all( )


  var template_urls = [
    './templates/main.template.html',
    './templates/stages.template.html',
    './templates/data_loader.template.html',
    './templates/data_mapper.template.html',
    './templates/customize_chart.template.html',
    './templates/publish_embed.template.html',
    './templates/layer_list.template.html',
    './templates/property_list.template.html',
    './templates/property_detail.template.html',
    './templates/input.template.html',
    './templates/mark.template.html',
    './templates/select.template.html',
    './templates/color_schemes.template.html',
    './templates/padding.template.html',
    './templates/tooltip.template.html',
    './templates/external_url.template.html'
  ]

  Promise.all(
    template_urls.map(function(url) {
      return fetch(url)
    })
  )
    .then(function(responses) {
      return Promise.all(
        responses.map(function(res) {
          return res.text()
        })
      )
    })
    .then(function(tmpls) {
        ....
        ....

Promise.all( )

function dependency_accumulator(current_template, self, data, options) {
  if ($(current_template).data('subtemplates') === undefined) {
    return Promise.resolve(100)
  } else {
    var dependency_selectors = $(current_template)
      .data('subtemplates')
      .split(',')
      .filter(function (selector) {
        return !$(selector).data(_compiled)
      })

    return Promise.all(
      dependency_selectors.map(function (dependency_selector) {
        return dependency_accumulator(dependency_selector, self, data, options)
      })
    ).then(function () {
      return Promise.all(
        dependency_selectors.map(function (sub_temp_selector) {
          return execute_render($(sub_temp_selector), data, options)
        })
      )
    })
  }
}

Promise.all( ) Gotchas!

var promise_calls = ['bar-vega.json', 'line-vega.json'].map(fetch)
Promise.all(promise_calls)
    .done(function (results) {
      // results is an array of the values stored
      //  in a.json and b.json
    }, function (err) {
      // If any of the files fails to be read, 
      // err is the first error
    });

Promise.allSettled( )

Promise methods

.all - wait success of all, fail if just one

.allSettled(arr) - wait for all promises to settle (either with a value or with an error, doesnt matter)

.race(arr) - wait for 1 promise to succeed, fail if just a single one fails

.any(arr) - wait for success of 1, fail when impossible to fulfill 1 (only when everyone fails)

 

 

.some(arr, N) - wait success of N, fail when impossible to fulfill N (because arr.length - N + 1 have failed)

 

 

Bluebird Promise library supports

The goal is to cancel it if requestData succeeded. "There is a different UI active now and it would be pointless to try and process things further towards rendering"

 

 

Promise
    .race([waitSomeRequest(), stillSameRoute()])
    .then(renderDataFromRequest);

.race(arr) - wait for 1 promise to succeed, fail if just a single one fails

Error Handling using .catch()

function asyncTask(url) {
  return new Promise((resolve, reject) => {
    if (url) {
      return setTimeout(() => {
        resolve(asyncTask2());
      }, 500);
    }

    reject({
      error: 'url missing in async task 1'
    });
  });
}

function asyncTask2(url) {
  return new Promise((resolve, reject) => {
    if (url) {
      return resolve({
        id:2
      });
    }

    reject({
      error: 'url missing in async task 2'
    });
  });
}

asyncTask('google.com')
  .catch(err => {
    console.log('failed ', err); // { error: 'url missing in async task 2' }
    return err;
  });

Error Handling in Nested Promises

function asyncTask(url) {
  return new Promise((resolve, reject) => {
    if (url) {
      return setTimeout(() => {
        resolve(asyncTask2());
      }, 500);
    }

    reject({
      error: 'url missing in async task 1'
    });
  });
}

function asyncTask2(url) {
  return new Promise((resolve, reject) => {
    if (url) {
      return resolve({
        id:2
      });
    }

    reject({
      error: 'url missing in async task 2'
    });
  });
}

asyncTask('google.com')
  .catch(err => {
    console.log('failed ', err); // { error: 'url missing in async task 2' }
    return err;
  });

Error Handling in Promises

// covers promise constructor in asyncTask and 3 then
asyncTask()
  .then()
  .then()
  .then()
  .catch() 


asyncTask()
  .catch() // only covers promise constructor in asyncTask
  .then()
  .then()
  .catch() // 2 then

Thank you!

Questions?

JS Promises

By Tejesh P

JS Promises

  • 81