Why Promises?

Andrew
Tech at Power
Published in
5 min readAug 10, 2020

--

Photo by Ashim D’Silva on Unsplash

Asynchronous Programming

Back in like the 1500’s or something you could only get a computer to execute one thing at a time. Then more recently (1965), the idea of processes started to be used in computing. Processes run the gambit of storing data to disk or running some code to output things. Processes gave us the illusion of having more than one thing running at a time — it’s really just context switching between processes that makes it possible for me to be running a web browser and listening to music at the same time.

Threads

Processes are great, but some tasks weren’t optimized for being a process. Computers were starting to be able to accomplish more and more tasks at a time, which could slow everything down since processes were blocking other processes. Enter threads and task schedulers. Threads are components of a process that execute commands that are handled by the task scheduler. This makes it so individual processes can run multiple commands using a singe shared resource. In less complex terms, big thing can now do a bunch of tiny things using the big thing’s resources.

Problems with Threading

Threading solves a lot of issues with timing but causes a whole bunch of others, mainly race conditions. Race conditions arise when a shared resource is either unexpectedly mutated or is empty when needed. For an example, let’s say I have a program that can calculate the nth Fibonacci number in C++

typedef unsigned long int uli;
uli fib(uli n){
if(n <= 1){
return n;
else{
return fib(n - 1) + fib(n - 2);
}
}

If I want to write a program that will find a few nth primes concurrently and output the result

void fibThread(uli n) {
std::cout << n << “ fib number is: “ << fib(n) << std::endl;
}
void fibThreads() {
uli a = 5, b = 35;
std::thread tb(fibThread, b);
std::thread ta(fibThread, a);
ta.join();
tb.join();
}

This works in main however it will output the results in the following order

This will get us into some hot water if we relied on b for something before we needed a to finish executing… This is where somethings called futures and promises come in to save the day!

Promises and Futures

Futures and promises help deal with this uncertainty by providing an object that is responsible for setting a value of some type T for retrieval by a future object — essentially a setter and a getter! A promise sort of acts as a placeholder for an object in this regard. It says to the program ‘this will be something some day, but right now we don’t know what’ while a future asks the promise for a value and will wait until the value is set. In the C++ example, we’d have:

void findNthFib(std::promise<uli>&& FibFoundPromise, uli n) {
FibFoundPromise.set_value(fib(n));
}

void fibPromises() {
uli a = 5, c = 35;

std::promise<uli> FibPromiseA;
std::future<uli> FutureA = FibPromiseA.get_future();
std::promise<uli> FibPromiseC;
std::future<uli> FutureC = FibPromiseC.get_future();

std::cout << "Creating thread for value " << c << std::endl;
std::thread t1(findNthFib, std::move(FibPromiseC), c);
std::cout << "Waiting for result..." << std::endl;
std::cout << c << " fib number is: " << FutureC.get() << std::endl;

std::cout << "Creating thread for value " << a << std::endl;
std::thread t2(findNthFib, std::move(FibPromiseA), a);
std::cout << "Waiting for result..." << std::endl;
std::cout << a << " fib number is: " << FutureA.get() << std::endl;

std::cout << "Done!" << std::endl;
t1.join();
t2.join();
}

Which would produce the result (together with the snip from threading)

These patterns are seen in many different programming languages with threading. But “Hold on”, you say, “isn’t javascript single threaded and aren’t you supposed to be talking about it and not C++?” Yes.

Javascript Promises

Since Javascript is single threaded, we don’t have to worry about the craziness of threading and the memory management that comes with it, praise the sun. However, we do have to think about how Javascript executes code.

Single Threaded, Nonblocking I/O

The simplest way to explain is to think of Javascript execution as two data structures: a call stack and an event queue. Synchronous tasks are handled in the call stack, ex Math.ceil(itemPrice);. Asynchronous events are pushed into the event queue one-by-one and executed with the event loop.

const addTwoSync = (a,b) => a + b;
const addTwoAsync = (a,b) => setTimeout( () => a + b, 0);
addTwoAsync(2,2);
addTwoSync(2,2);

Javascript deals with the code in the following way:

  1. The two functions are interpreted
  2. addTwoAsync is pushed onto the event queue for later evaluation
  3. addTwoSync is called, call stack loads a and b, performs an operation, and returns the result
  4. The call stack is empty, so the event loop starts to pick up things from the event queue
  5. addTwoSync is called, and executed similar to step 3

That’s essentially how it works without going into more than extremely basic detail. Now Javascript does this cool thing where everything that is async returns a Promise by default. Js has the keywords async and await for processes that need be sent to or retrieved from the event queue. async will make the function return a Promise and await will halt the execution until the promise contains the type (or error) once the promise settles and isn’t in the pending phase. This mirrors the C++ future and promise, just in a different context of the event loop!

Promise Syntax

Promises have a resolve and a reject fate that you can use to wrap the result in. For example,

const p = new Promise( (resolve, reject) => true ? resolve(‘True’) : reject(‘False :(`);

This is a promise that will resolve to a string that says ‘True’. To unwrap the value of a promise

p.then( (res) => console.log(res) , (err) => console.log(‘Error: ‘, err));

The then has two functions the first of which will be the resolve state and the second the rejected. Alternatively you can write it like so

p
.then( (res) => console.log(res))
.catch( (err) => console.log(‘Error: ‘, err));

This is the more common syntax for promises (at least that I’ve seen).

Wrap up

Promises are essentially wrappers for values that have not been retrieved or may take a while to compute. They have been around for a while and will be here as long as we have asynchronous programming.

This post is written by a Power Code Academy Student Developer.

--

--