Git Product home page Git Product logo

javascript-promises's Introduction

Promises

Objectives

By the end of this lesson you should be able to:

  • Use arguments to inspect the arguments that are passed to then
  • Refactor async code (that supports promises) to use promises instead of callbacks using then
  • Describe how then is chainable and how data moves from one then to the next, especially when then returns a promise
  • Write chained then promises
  • Use Promise.all to wait for an array of promises to resolve

Set the stage

Why?: Callbacks can become difficult to work with when you are making several asynchronous calls. Promises are a common and awesome way to help make asynchronous code easier to work with.

Promises can be challenging to reason about at first. Keep a sunny disposition and calmly and methodically work your way through. Pause to come up with your own examples, refactor existing code to promises, find an watch new videos etc... Just sort of live in and around promises for a little while. If this takes you a week - no worries!

Promises are becoming as fundamental as Strings or other data types in JavaScript. They appear in almost every JavaScript framework and will play an increasingly important role in JavaScript in the future.

Activities

#1 - Read up a little

OK - so promises make it so you can turn this code:

$.getJSON('/stuff', function (data) { })

Into this:

$.getJSON('/stuff').then(function (data) { })

See the difference? The second one has a subtle, but incredible powerful difference. This might make it clearer. Take this code:

var makeRequest = function (cb) {
  $.getJSON('/stuff', function (data) {
    cb(data)
  })
}

makeRequest(function (data) {
  // do stuff with data
})
var makeRequest = function () {
  $.getJSON('/stuff')
}

var promise = makeRequest();
promise.then(function (data) { })

See what happened there? You can now return a promise, which means you don't have to pass in a callback.

Astute Developer: Uh... big deal right? You just changed where the callbacks were. Seems like you are rearranging deck chairs on the Titanic here.


Seasoned Developer: Be patient little grasshopper. I promise you'll see (yuk! yuk!). See what I did there? I said "I promise"...


Astute Developer: oh brother... OK, move on please

#2 - Understand then by experimentation

Imagine a Mongo / Monk script that needs to:

  • Remove all records
  • Once all records are removed, add a new record
  • Once that record is inserted, find all records and print them out

That code will look like this:

users.remove({}, function (err) {
  users.insert({name: 'Joe'}, function (err, result) {
    users.find({}, function (err, results) {
      db.close()
    })
  })
})

So how do you convert that to use promises? It turns out that secretly Monk already returns promises! Actually, it's not so secret - it's right there in the docs. What does that mean?

Go into 01_playground.js, add the following line and run the file:

console.log(users.remove({}));

The output will look something like:

{ col:
   { manager:
      { driver: [Object],
        helper: [Object],
        collections: [Object],
        options: [Object] },
     driver:
      { _construct_args: [],
    //...

That output isn't too helpful, but it proves something. It proves that users.remove() returns something - which is key. What does it return? Let's go to the docs to find out:

https://github.com/Automattic/monk#promises

Hmm... Promises should have a then method, but those docs don't show any then method. Let's be brave and see if one exists. Add the following to the top of 01_playground.js

var promise = users.remove()
console.log(promise.then.toString());

What's happening in that snippet? Take a second to write out what you think is going on:

YOUR ANSWER HERE

So it turns out that Monk methods do indeed return a promise that supports then, so we can make use of that by changing the callback to a then:

users.remove({}, function (err) { })

// becomes

users.remove({}).then(function () { })

What gets passed to then?? Monk isn't clear at all about that (in fact, there are NO references to the word "then" anywhere in the docs). So let's find out by inspecting the arguments object that's available in every method:

users.remove({}).then(function () {
  console.log(arguments);
})
// prints { '0': 1 }

users.insert({name: 'Harry'}).then(function () {
  console.log(arguments);
})
// { '0': { name: 'Harry', _id: 55ae69439c31b8a2228f9780 } }

In both cases the then function is being passed a single argument (which you can see because the arguments object has a single key in both cases: "0").

NOTE: if you forgot what arguments is, read up on arguments over at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments and then come back...

So now you know, just from experimentation, that you can add a parameter to your callback function:

users.remove({}).then(function (x) {
  console.log(x);
})
// 1 (or 0 or some other number...)

users.insert({name: 'Harry'}).then(function (x) {
  console.log(x);
})
// { name: 'Harry', _id: 55ae69439c31b8a2228f9780 }

Now you should figure out what to name those variables. In order to name a variable, you need to know what it represents. In the remove case, what do you think that number 1 means? Well, you know this is coming from an underlying Mongo command "remove", so check the docs for remove here: http://docs.mongodb.org/manual/reference/method/db.collection.remove/#db.collection.remove

What does remove return in Mongo? Look around that page until you find it. Look carefully and you'll see that it returns a writeResult - but what's that? Go read the docs for writeResult and then come back.

So it looks like the callback you pass to users.remove().then() gets called with the number of records that were removed. Let's experiment with that by inserting two records and seeing the results:

users.insert({name: 'Harry'}).then(function () {
  users.insert({name: 'Sally'}).then(function () {
    users.remove({}).then(function () {
      console.log(arguments);
    })
  })
})

Yup. When you call then on users.remove it returns the number of records that were deleted. What about the callback you pass to insert? What does that return?

{ name: 'Harry', _id: 55ae69439c31b8a2228f9780 }

That looks like the whole doc. SO - now you know via experimentation that the then callback will get called with different arguments based on the specific call you are making, and you know that when in doubt, you can use the arguments object to inspect those.

#3 - Reflect

From reading and experimenting, what do you know about Promises and then? Take a minute to write your emerging understanding here:

YOUR THOUGHTS HERE

#4 - Convert simple callbacks

Ready to code yet? Awesome!

  1. Run 02_monk.js and inspect the output
  2. Open 02_monk.js and change the code from callbacks to promises.

Each Monk command returns a promise. So the first iteration might look like this:

users.remove({}).then(function (err) {
  users.insert({name: 'Joe'}, function (err, result) {
    users.insert({name: 'Sue'}, function (err, result) {
      users.insert({name: 'Tim'}, function (err, result) {
        users.insert({name: 'Kim'}, function (err, result) {
          users.find({}, function (err, results) {
            console.log("\nSuccess! The records are: \n");
            console.log(results);
            db.close()
          })
        })
      })
    })
  })
})

Once they are all using .then instead of callbacks, run the file to make sure the output looks the same, and then move on.

Chain'em up!

Unlike callbacks, you can chain promises if you do it right. Here's an example:

users.remove({}).then(function () {
  return users.insert({name: 'Joe'})
}).then(function () {
  return users.insert({name: 'Sue'})
}).then(function (records) {
  console.log(records);
  db.close()
})

What's going on there? You could say:

"Remove all users. When the records are removed, then insert Joe. When joe is done being inserted, then insert Sue etc..."

Using similar language, how would you describe the following code?

messages.remove({}).then(function () {
  return users.remove({})
}).then(function () {
  return users.insert({name: 'Joe'})
}).then(function (joe) {
  return messages.insert({senderId: joe._id})
}).then(function (message) {
  console.log("Message was inserted!", message);
  db.close()
})

YOUR ANSWER HERE.

Take a look at your answer above. Does it account for the fact that the third callback gets joe passed to it? How did that happen?

If you had to describe chained thens to another beginning developer, how would you do it based on the code above?

Refactoring AJAX Calls

One very common usage of Promises is to refactor AJAX calls. Let's get started!

Setup

  1. Start your server with http-server -c-1 -o
  2. Open the console.

Click each link, look at the output on the page and look at the console output. The goal of a refactor is that it doens't change the way the app works - it's just a pure code reorganization. You have 3 challenges here:

  1. Refactor all $.getJSON functions to take advantage of the fact that $.getJSON returns a promise
  2. Refactor the "Get First" and "Get Last" functions to use chained promises
  3. Remove the duplication from "Get First" and "Get Last" by writing your own function that returns a promise that has access to all the data needed.

THIS IS NOT SIMPLE! If you are new to Promises, those three challenges will ramp up steeply in difficulty. Make a new branch, spend some time on it, try to figure it out. Come to instructors if the instructions aren't clear, and then if you still can't get it, we'll do a workshop on it :)

Promise.all

Another common scenario is that you have multiple things happening concurrently. This is a great use for Promise.all

Optional: Write your own

Ready for more? You can write your own promises (instead of just using promises returned by other frameworks).

See https://www.promisejs.org/ for more details. Then refactor all of the functions in HTML to use XMLHttpRequest instead of $.getJSON, by writing your own promise wrapper around XMLHttpRequest

Have fun!

Reflect

Go back to the objectives. How would you rate yourself on each? If it's less than a 3-out-of-4, let's talk!

Reflect: New Questions

What new questions do you have? Write down at least 4 (c'mon, I know you have them):





Look into the future

https://www.youtube.com/watch?v=DqMFX91ToLw

javascript-promises's People

Contributors

j-brenneman avatar

Stargazers

Tricia O'Toole avatar

Watchers

James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.