This note is my attempt at explaining the idea of a promise object in JS.
Learning javascript as the second language, right after Ruby, is sometimes hard, as the path from zero to JS-hero is full of new - unknown to Ruby - concepts. One of them is a promise object that I happened uppon while creating my first end-to-end JS application.
For an MVP I was supposed to write an application which:
- allows user to add posts (listings) on the website,
- and display the listings on the website,
so basically the first two steps of the CRUD cycle.
- Node.js, express.js, sequelizer, postgres.
- Listings controller
This file contains two CRUD methods for the listing model, one to create a record in the database and one for retrieve all of the records.
const Listing = require('../models').Listing;
module.exports = {
create(req, res) {
return Listing
.create({
name: req.body['listingname'],
location: req.body['listinglocation'],
description: req.body['listingdescription'],
price: req.body['listingprice']
})
}, //I've used body-parser to get the data from a html form
list(req, res) {
return Listing
.all()
},
};
- Router
This file contains two routes:
app.get
, which is rendering the index.ejs with the listings retrieved from the database,app.post
, which is saving the records to the database.
const listingsController = require('../controllers').listings;
var path = require('path');
module.exports = function(app) {
app.get('/', function(req, res) {
listingsController.list(req, res)
.then(function(listings){ // 'then' is crucial for this note
res.render('index', { x: listings })
});
});
app.post('/', listingsController.create);
}
In order to explain the code above I'll use the following diagram.
CREATE
+---------------------------------------------+
|
+-------------+ HTTP +--------------+ |
| | | | |
| BROWSER | +------> | ROUTER | |
| | | | |
+-------------+ +------+-------+ |
| |
+----------------------+ | |
| v |
<-----------------+ | +------+-------+ |
LIST | | | | |
| | | CONTROLLER | |
| | | | |
| | +------+-------+ |
| | | |
| | | |
| | v |
| | +------+--------+ |
| | | | |
| | | MODEL | |
| | | | |
| | +-------+-------+ |
| | | |
| | | SQL |
| | v |
| | |
| | +---+ |
| | | | |
| | | | |
| | |DB | |
| +------> | | <-----+
| | |
+-----------+ +---+
As you can see (hopefully), the create method goes one way - we want to store a new record in a database and that's it. We don't want to get anything back. Let's analyse it step by step:
- On a homepage there is a following form:
<form class="form" action="/" method="post">
<p2> Add a place: </p2>
<input type="text" name="listingname" placeholder="listingname" class="single_form_window">
<input type="text" name="listinglocation" placeholder="location" class="single_form_window">
<input type="text" name="listingprice" placeholder="price" class="single_form_window">
<input type="text" name="listingdescription" placeholder='description' class="single_form_window description">
<input type="submit" value="submit">
</form>
It sends us to /
with a post
method.
- router, seeing this, calls a
listingsController.create
function:
// in router
app.post('/', listingsController.create);
- This function picks the data from the form and
create
a new record in the database:
// in a controller
const Listing = require('../models').Listing;
module.exports = {
create(req, res) {
return Listing
.create({
name: req.body['listingname'],
location: req.body['listinglocation'],
description: req.body['listingdescription'],
price: req.body['listingprice']
})
}
- And that's it. We don' want anything back, just want to see our record in the database.
*5) Side note - read after completing the whole note: create
method also returns a promise object, but I'm ignoring it in my program as in my create
function I just want to save the record in the database and don't want to get anything back. *
The read element of CRUD (list
method) is more complicated.
-
We want a user to be able to see the records on the website right at the start. That's why at the
app.get('/')
we want to get all of the listings. -
For this purpose I'm using the
listingsController.list(req, res)
functions that will get the data from the database and pass it to the index page.
// in router
app.get('/', function(req, res) {
listingsController.list(req, res)
.then(function(listings){
res.render('index', { x: listings })
});
});
- The
list
function is pretty straightforward
// in a controller
list(req, res) {
return Listing
.all()
},
-
What's interesting, this method is not returning all our records, but only a promise object. This promise object will become the data only when we trigger it.
-
For this purpose we use
.then
. -
Let's analyze the following piece code step by step. I'm chaining the methods now for educational purposes:
listingsController.list(req, res).then(function(listings){
res.render('index', { x: listings })
});
- we call the
list
method which returns us the promise object, - and right after that we activate the data by calling
.then
. - at the end we
render
theindex
page and pass the data to later use them on the index page.
JS operates in the browser. As users usually do/want may operations at the same time, JS needs to be prepared for this. In order to explain it we can use the following analogy.
JS is a wedding planner and wants everything to be ready at the right time. Because of this, before a wedding cake enters the ballroom, it sends a person to buy this cake and wait for a right moment to enter.
The cake will enter a room only when asked by the wedding planner. So think of .this
as of a call from the wedding planner to that helping person.