Published on

Futures in Concurrent Programming (with examples in JavaScript)

Authors
  • avatar
    Name
    Benton Li
    Twitter

Promise me no promises

— Demi Lovato

Prerequisite:

  • Some knowledge of programming
  • Some knowledge of operating systems

Multitasking

We, humanly named Homo Sapiens, are born to multitask. For example, when you make yourself a Chicken Alfredo, you might need to do the following tasks:

  • Boil the water
  • Break the pasta in half and cook pasta when water is boiled
  • Cook the chicken
  • Ensemble them

How do you multi-task? Well, you can boil the water first; while it’s boiling, you cook the chicken.

Multitasking in essence is doing multiple tasks (cooking) using a single resource (your kitchen)

In computer science, multitasking is in the form of multi-threading.

You run multiple threads using a single CPU (or more)

n.b. There is also multi-processing (multiple processes using a single machine/data center/cloud), but we are not going to talk about their difference here. Take Operating Systems!

Synchronous & Asynchronous

Now you notice that some tasks can be synchronous and asynchronous. In short, synchronous tasks are those that might block other tasks whereas asynchronous tasks are generally non-blocking (i.e. can run in parallel while minding their own business)

For instance, imagine you are a poor first-year analyst at Storgan Manley.

  • Synchronous tasks: Today you need to make slides, send them to your manager for review, modify the slides, and then send them back to your managers. These tasks are synchronous. You cannot modify the slides until your manager (probably on vacation in Barbados) finishes reviewing them, so you wait.
  • Asynchronous tasks: You need to download a gigantic dataset, but now it’s also lunchtime. You want coffee from Cafe 750 (4th floor) and lamb over rice™️ from the food truck (ground floor). You can
    1. Start downloading the dataset,
    2. Then order coffee on the Thrive app,
    3. Then get the food, (coffee should be done at the moment)
    4. Then get the coffee, (the dataset should be ready)
    5. Finally back to the desk, devouring the scrumptiousness in front of your quad-monitors

The beauty is that you can do some tasks while waiting for other tasks to finish

What characterizes asynchrony? Well, asynchronous events are kind of unpredictable. Your computer doesn't know when you will click your mouse. Your Excel doesn’t know when the BQL query result will be returned. You don’t know when your manager will vibe-check you, etc.

The question is: how do you abstract asynchronous programs?

Promise in JavaScript

As mentioned, asynchrony infers parallelism and interleaving. This leads us to concurrent programming, which is essential in Machine Learning and web development. Here comes one of our main characters: Promise

Perhaps when you start to learn JavaScript for the first time, keywords like Promise, async, and await, will give you some aha moments.

A Promise is an abstraction of a strategic agent who will yield some results (though you might not get the result. By being strategic I mean having several plans in the event of success, failure, or other outcomes. In other words, a promise is a combination of executor and handlers (to reject, to resolve, or to do something else).

For a simple example, when an agent is dispatched for a mission, they could have 3 statuses: PENDING (on the way), RESOLVED (finished the job), or REJECTED (got fucked). Based on the result RESOLVED or REJECTED, the agent carries out their corresponding plans. Perhaps you will send a new agent, perhaps you dismiss this agent, or perhaps you let this agent retry.

Below is an overview of a Promise

/static/images/promise/promise.jpg

Courtesy of MDN Web Docs

“Promise“ an omelet

When you cook an omelet, you are making a Promise. If you overcook it, you might throw it away and say “fuck it”. If it’s well done, you chomp it.

In JavaScript, it’s simply this (try it in your web browser console!)

const cookEgg = (resolve, reject) => {
  setTimeout(() => {
    const randNum = Math.random()
    var egg = 'raw egg'
    if (randNum < 0.5) {
      egg = 'omelet'
      resolve(egg)
    } else {
      egg = 'charcoal'
      reject(egg)
    }
  }, 500)
}

const eggGood = (egg) => {
  console.log(`CHOMP, yummy ${egg}`)
}

const eggBad = (egg) => {
  console.log(`fuck this ${egg}`)
  // throwAway(egg)
}

new Promise(cookEgg).then(eggGood).catch(eggBad)

Here: cookEgg is the your execution and the variable egg is your result. You have a chance 0.5 to succeed and call resolve right after. However, you also have a chance of 0.5 to fail, which leads to calling reject

In the last line, we fire this Promise, dispatching an agent to do cookEgg. If the task is resolved, you will call resolve/eggGood with your result/egg; otherwise, you will call reject/eggBad with your result/egg.

In the view of your operating system, you just created a thread.

n.b. This is a simple demonstration. You can do advanced thing like chained promises and promise pools, but it’s not of our interest in this post.

n.b. Keep this question in mind: how do I keep the egg?

FAQ:

Q: Is a Promise an object?

  • A: Yes. And calling new Promise(…) creates a new Promise object, or a thread, precisely

Q: We called new Promise(some_function), now what?

  • A: In the term of compiler, this is an expression. But it will executes some_function right away. If you do const p = new Promise(some_function) , it will do the same thing, but store the promise in p.

Q: After we called new Promise(some_function), does the program have to wait for some_function to finish?

  • A: Depends. It doesn’t have to block, unless you add a await . See examples below
    const makeSnack = new Promise(cookEgg).then(eggGood).catch(eggBad)
    console.log('Drink a coke')
    // You will drink a coke first, while waiting for your egg to be done
    
    const makeSnack = await new Promise(cookEgg).then(eggGood).catch(eggBad)
    console.log('Drink a coke')
    // You have to wait for the egg to be done first, then you drink a coke
    

Q: Why can’t I just write an if statement within a function?

  • A: Promise has the asynchronous feature. It is to some sense doing if statement, but now you can define whatever executions in if-else branches as function calls. Notice that both a function call and an block of an if statement are both statements.

    if (...){
    	// These are statements!
    }
    
    func(){
    	// These are statements too!
    }
    

More Examples: fetch

Think about data fetching, where fetch returns a promise. Data fetching looks like this

/static/images/promise/fetch.jpg

Courtesy of MDN Web Docs

For example, let’s fetch the latest reference rates from New York Fed and print them.

fetch('https://markets.newyorkfed.org/api/rates/all/latest.json')
  .then((res) => res.json())
  .then(console.log)
// You will see something like this
// {
//     "refRates": [
//         {
//             "effectiveDate": "2023-12-29",
//             "type": "SOFRAI",
//             "average30day": 5.34407,
//             "average90day": 5.35531,
//             "average180day": 5.34725,
//             "index": 1.11461178,
//             "revisionIndicator": ""
//         },
// 			...
//     ]
// }

Cool, information printed, but where do we keep them? Silly way: declare a global variable.

var rate_data
fetch('https://markets.newyorkfed.org/api/rates/all/latest.json')
  .then((res) => res.json())
  .then((data) => (rate_data = data))

But, can we do this more elegantly? How can we extract the results from the Promise (how do we get the egg)?

As a quant, I probably want something like

sofr_data = getSOFT(...)
sonia_data = getSONIA(...)
// wait until sofr_data and sonia_data are ready
doMachineLearningStuff(sofr_data, sonia_data)

It is implementable in JavaScript, but it won’t be elegant. Perhaps you can think of using useState and useEffect in React.

Future or Promise?

If you are a Java/Scala programmer, perhaps you would ask, “wait a minute, the Promise in JS doesn’t looks like a Promise in Scala, but more like a Future!”

Sadly it’s true. According to Barbara Liskov, a promise is “a place holder for a value that will exist in the future”. So in this sense, JS’s Promise is more like a future.

Here is the reference.

JavaScriptScala/Java
DeferredPromise/CompletableFuture
PromiseFuture

Read more

Chapter 8.6: Promises, OCaml Programming: Correct + Efficient + Beautiful

Implementation of Promise in JS

Barbara Liskov's paper on Promise

Next blog: Future in Scala


Just for fun

We can fetch multiple APIs at the same time by using Promise.all

Let’s fetch the latest reference rate and volume from New York Fed and print them!

Promise.all([
  fetch('https://markets.newyorkfed.org/api/rates/all/search.json?type=rate').then((resp) =>
    resp.json()
  ),
  fetch('https://markets.newyorkfed.org/api/rates/all/search.json?type=volume').then((resp) =>
    resp.json()
  ),
]).then(console.log)