Promises and Async

Asynchronous Programming

Asynchronous and Synchronous are two behaviours, in programming terms asynchronous was deemed the more problematic as the delay for something to return was indeterminate from may be milliseconds or even seconds, synchronous on the other hand simply waited until data was returned however this mean't that the problem paused.

In the past Javascript used timeouts to mimic asychronous programming, also you used to use try-catch blocks to handle any issues or errors, this type of programming was called the callback from hell and was not scaleable. The new feature to fully support asynchronous programming was called promises.

Promises

A promise is an object that may produce a single value some time in the future, the retruned data may be what you requested or an error could occur (a network error occurred, database error). A promise may be in one of 3 possible states, fulfilled, rejected, or pending. You can attach callbacks to a promise to handle the returned data or handle the failure.

So why use a promise, firstly its to abstract the block of asynchronous code execution, to allow the program to continue on its merry way, for example you may want to retrieve so data from an external source, you can do this with a promise and still continue creating the web page while we wait for the data to be returned to finish off the web page. Also because the state of the promise is private you can manipulate the data returned before displaying it on the web page.

The promise execution has two primary duties initialize the asynchronous of the promiseand controlling any state transition. One either function is executed there is no going back to the pending state, the two functions used are resolve and reject, also yo avoid the promise getting stuck you add a timed exit.

Promise example
let p = time => new Promise((resolve, reject) => setTimeout(resolve, time));
p(2000).then( () => console.log('Hello!'));

You can directly call either the resolve or reject

Resolve and Reject
setTimeout(console.log, 10, Promise.resolve(func1()));
setTimeout(console.log, 0, Promise.reject(error()))

The promise type implements a thenable interface which you can attach handlers, the then() method is used to attach handlers and it accepts two arguments an optional onResolved handler and an optional onRejected handler.

then() method
function onResolved(id) {
    setTimeout(console.log, 0, id, 'resolved');
}
function onRejected(id) {
    setTimeout(console.log, 0, id, 'rejected');
}

let p1 = new Promise((resolve, reject) => setTimeout(resolve, 3000));
let p2 = new Promise((resolve, reject) => setTimeout(reject, 3000));

p1.then(() => onResolved('p1'), () => onRejected('p1'));
p2.then(() => onResolved('p2'), () => onRejected('p2'));
then() method (optional resolve/reject)
function onResolved(id) {
    setTimeout(console.log, 0, id, 'resolved');
}
function onRejected(id) {
    setTimeout(console.log, 0, id, 'rejected');
}
let p1 = new Promise((resolve, reject) => setTimeout(resolve, 3000));
let p2 = new Promise((resolve, reject) => setTimeout(reject, 3000));

// Non-function handlers are silently ignored, not recommended
p1.then(() => onResolved('p1'), null);

// Canonical form of explicit onResolved handler skipping
p2.then(null, () => onRejected('p2'));
Explicit return values
let p1 = Promise.resolve('foo');
setTimeout(console.log, 0, p1);

let p2 = p1.then(() => 'bar');                      // new onResolved handler
setTimeout(console.log, 0, p2);
catch() method
let p = Promise.reject();

let onRejected = function(e) {
    setTimeout(console.log, 0, 'rejected');
};

// These two reject handlers behave identically:
p.then(null, onRejected);                                     // rejected
p.catch(onRejected);                                          // rejected


Note: catch() method can be used to attach only a reject handler to a promise
finally() method
let p1 = Promise.resolve();
let p2 = Promise.reject();

let onFinally = function() {
    setTimeout(console.log, 0, 'Finally!')
}

p1.finally(onFinally);                          // Finally
p2.finally(onFinally);                          // Finally


Note: finally() method can be used to attach an OnFinally handler which executes when a promise reaches either a resolved or rejected state.

Promise Chaining

You can combine multiple promises together which is called chaining, promises are strictly sequenced, each of the promises instance methods then(), catch() and finally() returns a separate promise instance which in turn can have another instance method called upon it.

Promise chaining (basic example)
let p = new Promise((resolve, reject) => {
    console.log('first');
    resolve();
});

p.then(() => console.log('second'))                       // onResolved defined
 .then(() => console.log('third'))
 .then(() => console.log('fourth'));
Promise chaining (more advanced example)
function delayedResolve(str) {
    return new Promise((resolve, reject) => {
        console.log(str);
        setTimeout(resolve, 1000);
    });
}

delayedResolve('p1 executor')
    .then(() => delayedResolve('p2 executor'))
    .then(() => delayedResolve('p3 executor'))
    .then(() => delayedResolve('p4 executor'))
Putting it all together
function func1() {
    console.log("Success");
}

new Promise(() => func1())
    .then(val => console.log(val))                                     // success
    .catch(err => console.log("Failed!"))
    .finally(() => console.log("Promise complete!"))                   // Promise complete!

-----------------------------------------------------------------------
new Promise(() => undefined.get())
  .then(val => console.log(val))
  .catch(err => console.log("Failed!"))                                // Failed!
  .finally(() => console.log("Promise complete!"))                     // Promise complete!

You can also use advanced parallel features such as all() and race() methods.

Async Functions

Async is normally paired with wait and are ES7 (and above) promise paradigm, you use the keyword async which can be used on functions declarations, function expressions, arrow functions and methods.

Async keyword
async function foo() {}

let bar = async function() {};
let baz = async () => {};

class Qux {
    async qux() {}
}

Async has some asynchronous characteristics but its still synchronously evaluated, when used with a function a promised is returned and the return statement is the Promise.resolve().

Async and Function
async function hello() {
    console.log("1");
    return 3;                               // this is the Promise.resolve()
}

hello().then(console.log);                 // a promise is returned
console.log(2);

The awaits keyword is used to pause the execution while awaiting for a promise to resolve.

awaits keyword
async function foo() {
    let p = new Promise((resolve, reject) => setTimeout(resolve, 1000, 3));

    console.log(p)              // pending
    console.log(await p);       // will wait until resolved
    console.log("Hello");       // will execute after above
}

foo();                          // 3
console.log("World!")           // will execute roughly after pending proofing asynchronous
sleep function using async/awaits
async function sleep(delay) {
    return new Promise((resolve) => setTimeout(resolve, delay));
}

async function foo() {
    let p = new Promise((resolve, reject) => setTimeout(resolve, 1000, 1));

    console.log(await p);       // will be displayed second (1 second wait)
    await sleep(5000);
    console.log("2");           // will be displayed last (7 second wait as above sleep)
}

foo();
console.log("3")                // will be displayed first

You can also use serial promise execution thus awaiting for a function to return before moing on

Serial promise execution
async function addTwo(x) {return x + 2;}
async function addThree(x) {return x + 3;}
async function addFive(x) {return x + 5;}

async function addTen(x) {
    for (const fn of [addTwo, addThree, addFive]) {
        x = await fn(x);
    }
    return x;
}

addTen(9).then(console.log);                // 19