Promise() – What the heck is a Promise? – That isn't the end!



Promise() – What the heck is a Promise? – That isn't the end!

0 0


promise-talk


On Github andrei-yanovich / promise-talk

Promise()

Created by Andrei Yanovich

Demo

What the heck is a Promise?

Promise is a special state storage object. At first it has - 'pending', after either 'fulfilled' or 'rejected'.

promise object can have 2 types of callbacks

  • onFulfilled - fires when promise is fulfilled
  • onRejected - when promise is rejected

Here is how we create them

var promise = new Promise(function(resolve, reject) {
  // do a thing, possibly async, then…
  if (/* everything turned out fine */) {
    resolve("Stuff worked!");
  }
  else {
    reject(Error("It broke"));
  }
}
            

A univesal API to set callbacks

promise.then(onFulfilled, onRejected)
            

if a promise was rejected/resolved then his state can't be changed anymore.

Promisifying XMLHttpRequest

function httpGet(url) {

  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);

    xhr.onload = function() {
      if (this.status == 200) {
        resolve(this.response);
      } else {
        var error = new Error(this.statusText);
        error.code = this.status;
        reject(error);
      }
    };

    xhr.onerror = function() {
      reject(new Error("Network Error"));
    };

    xhr.send();
  });
}
            

Now let's use it

httpGet('talk.json')
  .then(function(response) {
      console.log("Success!", response);
    }, function(error) {
      console.error("Failed!", error);
  });
            

That isn't the end!

You can chain then's() to transform values or run additional async actions one after another.

Transforming values

var promise = new Promise(function(resolve, reject) {
  resolve(1);
});

promise
  .then(function(val) {
    console.log(val); // 1
    return val + 2;
  })
  .then(function(val) {
    console.log(3); // 3
  });
            

More practical example

              httpGet('text.json')
  .then(JSON.parse) // json.parse takes one arg and returns json
  .then(function(response) {
    console.log("Yey JSON!", response);
  });
            

Async example

function getJSON(url) {
  return httpGet(url).then(JSON.parse);
}

getJSON('/data.json')
  .then(function(data) {
    return getJSON('api/user/' + data.userId);
  })
  .then(user => {
    console.log(user);
  });
            

If you return another promise inside then(), next then() in queue will be called only after your promise will be resolved.

Error handling

asyncThing1()
.then(function() {
  return asyncThing2();
}).then(function() {
  return asyncThing3();
}).catch(function(err) {
  return asyncRecovery1();
}).then(function() {
  return asyncThing4();
}, function(err) {
  return asyncRecovery2();
}).catch(function(err) {
  console.log("Don't worry about it");
}).then(function() {
  console.log("All done!");
});
              
This example was taken from html5rocks, because it rocks!

Static methods

  • Promise.resolve(thenable);
    Make a new promise from the thenable.
  • Promise.resolve(obj);
    Make a promise that fulfills to obj.
  • Promise.reject(obj);
    Make a promise that rejects to obj.
  • Promise.all(array);
    Make a promise that fulfills when every item in the array fulfills, and rejects if (and when) any item rejects.
  • Promise.race(array);
    Make a Promise that fulfills as soon as any item fulfills, or rejects as soon as any item rejects.

Examples

Promise.all([
  getJSON('/facebookApi/...'),
  getJSON('/twitterApi/...'),
  getJSON('/user/...')
]).then(function(arrayOfResults) {
// ...
})
            

Cache service calls

var searchTwitter = function(username) {
  var cache = {};
  if (!cache[username]) {
    cache[username] = getJSON('https://api.twitter.com/1.1/users/search.json?q=' + username);
  }

  return cache[username];
}
searchTwitter('famousPerson')
  .then(function(data){
    console.log(data.results);
    //Made a call
});
searchTwitter('famousPerson')
  .then(function(data){
    console.log(data.results);
    //Did NOT make a call
});
            

Prettify the code

var wait = function(duration) {
  return new Promise(function(resolve) {
    setTimeout(resolve, duration);
  })
};
wait(500).then(function() {
 console.log('Called!');
}); // will be called after 500ms
            

Promises are part of es6(es2015) standart. And they are already implemented in most browsers: Chrome 32, Opera 19, Firefox 29, Safari 8 & Microsoft Edge.

Anyway polyfill is here https://github.com/jakearchibald/es6-promise

Before the standart promises have been around in form of libraries, such as:

The above and other JavaScript promises share a common, standardised behaviour called Promises/A+.

jQuery have similar objects called Deferreds Despite deferreds aren't Promise/A+ compliant, they have rich functionality also.

Compatibility with other libraries

es6 promises will treat anything with a then method (so called thenable) as promise-like.

var q = Q.defer(),
    dfd = jQuery.Deferred();

Promise.all([q, dfd])
       .then(/* ..... */);
              

fetch API

fetch() is next gen XHR

var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';

xhr.onload = function() {
  console.log(xhr.response);
};

xhr.onerror = function() {
  console.log("Booo");
};

xhr.send();
              
fetch(url).then(function(response) {
  return response.json();
}).then(function(data) {
  console.log(data);
}).catch(function() {
  console.log("Booo");
});

//or with es6 arrows
fetch(url)
.then(r => r.json())
.then(data => console.log(data))
.catch(e => console.log("Booo"))
             

More examples

Post JSON

fetch('/users', {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Hubot',
    login: 'hubot',
  })
})
             

HTTP errors

function checkStatus(response) {
  if (response.status >= 200 &&
      response.status < 300) {
    return response
  } else {
    var error = new Error(
               response.statusText)
    error.response = response
    throw error
  }
}

function parseJSON(response) {
  return response.json()
}

fetch('/users')
  .then(checkStatus)
  .then(parseJSON)
  .then(function(data) {
   console.log('request succeeded, data)
  }).catch(function(error) {
    console.log('request failed', error)
  })
             

Streams

response.body is a ReadableStream

DEMO

Stream readers & cloning

fetch(url).then(function(response) {
  return response.json();
});
            
  • .arrayBuffer()
  • .blob()
  • .formData()
  • .json()
  • .text()

These are true stream readers, meaning they drain the stream

The call to .text() fails as the stream has already been read. You can work around this using .clone():

fetch(url).then(function(response) {
    return response.clone().json().catch(function() {
    return response.text();
  });
});
            

Further reading

https://jakearchibald.com/2015/thats-so-fetch/

fetch polyfill from github

Fetch specification

talk.resolve()

Promise() Created by Andrei Yanovich