- Published on
Futures in Concurrent Programming (with examples in JavaScript)
- Authors
- Name
- Benton Li
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 andcook 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
- Start downloading the dataset,
- Then order coffee on the Thrive app,
- Then get the food, (coffee should be done at the moment)
- Then get the coffee, (the dataset should be ready)
- 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
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 doconst 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 belowconst 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
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.
JavaScript | Scala/Java |
---|---|
Deferred | Promise/CompletableFuture |
Promise | Future |
Read more
Chapter 8.6: Promises, OCaml Programming: Correct + Efficient + Beautiful
Implementation of Promise in JS
Barbara Liskov's paper on Promise
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)