DEV Community

Cover image for JS interview in 2 minutes / Promise
Nick K
Nick K

Posted on

JS interview in 2 minutes / Promise

Question:
What is a Promise?

Quick answer:
It is an object which represents the current state and value of the operation. There are three states for Promises - pending, success, failure.

Longer answer:
Basically, the idea behind the Promises is fairly simple to understand. It is just a container that will only resolve when some calculations are done. That's it.

I guess it will be easier to understand if we just implement it on our own.

class MyPromise {
    dataHandlers = []
    errorHandler = []
    finalHandlers = []

    constructor(func) {
        // Apply handlers one by one and initialize every following handler with the previouses result
        let onResolve = data => this.dataHandlers.reduce(
            (acc, onData) => onData(acc), 
            data
        )
        // Just call every onReject
        let onReject = error => this.errorHandler.reduce(
            (_, onError) => onError(error),
            undefined
        )
        // Just call every onFinal
        let onFinal = () => this.finalHandlers.reduce(
            (_, onFinal) => onFinal(), 
            undefined
        )

        // We need to set timeout, so our function
        // executed after we set .then, .catch, and .finally
        setTimeout(() => {
            try {
                func(onResolve, onReject)
            } catch (error) {
                onReject(error)
            } finally {
                onFinal()
            }
        }, 0)
    }

    then(onData, onError) {
        if (onData) { this.dataHandlers.push(onData) }
        if (onError) { this.errorHandler.push(onError) }
        return this
    }

    catch(onError) {
        return this.then(undefined, onError)
    }

    finally(onFinal) {
        if (onFinal) { this.finalHandlers.push(onFinal) }
        return this
    }
}
Enter fullscreen mode Exit fullscreen mode

Let's test it!

let dataPromise = new MyPromise((resolve, reject) => resolve(2))
dataPromise
    .then(res => res + 2)
    .then(res => res * 2)
    .then(res => console.log(res)) // 8
    .finally(() => console.log('Finally!')) // Finally!
    .finally(() => console.log('Finally (1)!')) // Finally (1)!

let rejectPromise = new MyPromise((resolve, reject) => reject(2))
rejectPromise
    .then(res => res + 2)
    .then(res => res * 2)
    .then(res => console.log(res))
    .catch(error => console.error(error)) // 2
    .finally(() => console.log('Finally!')) // Finally!

let throwErrorPromise = new MyPromise((resolve, reject) => { throw new Error('hello') })
throwErrorPromise
    .then(res => res + 2)
    .then(res => res * 2)
    .then(res => console.log(res))
    .catch(error => console.error(error)) // hello
    .finally(() => console.log('Finally!')) // Finally

// This one will produce two errors instead of one.
// Can you come up with the fix?
let doubleErrorPromise = new MyPromise((resolve, reject) => reject('first'))
doubleErrorPromise
    .catch(error => { console.error(error); throw 'second' })
// 'first'
// 'second'
// Uncaught 'second'

// This is how it should work
let fixedDoubleErrorPromise = new Promise((resolve, reject) => reject('first'))
fixedDoubleErrorPromise
    .catch(error => { console.error(error); throw 'second' })
// 'first'
// Uncaught 'second'
Enter fullscreen mode Exit fullscreen mode

Real-life applications:
Sometimes it is a bit easier to use async/await syntax

And sometimes you will need Promise helpers functions like Promise.all

Resources:
MDN/Promise

Other posts:


Btw, I will post more fun stuff here and on Twitter. Let's be friends πŸ‘‹

Top comments (5)

Collapse
 
_bkeren profile image
''

I have a question.

How do onResolve , onReject and onFinal methods at the constructor wait for all 'then' or 'catch' methods to be called ? You set the timeout to 0 and invoke 'func', 0 means run the code immediately.

Collapse
 
hexnickk profile image
Nick K

Hey πŸ‘‹ Great question!

For example, if you use setTimeout, your function will be put into the execution queue, but not executed right away.

setTimeout(() => console.log(3), 0)
console.log(1)
console.log(2)
Enter fullscreen mode Exit fullscreen mode

will produce output

1
2
3
Enter fullscreen mode Exit fullscreen mode

even though console.log(3) appears before console.log(1) and console.log(2).

MDN/Even toop

Collapse
 
markiewiczjulian profile image
Julian Markiewicz • Edited

not precisely it means "run code in 0 seconds or more". This is because when running code with settimeout this is handled by timer API. Timer API waits given time (here 0 seconds) and then puts function (code that was contained within settimeout) in the message queue. Elements from message queue are run only if event stack is empty (this is far more complex than that and to get it I'm recommending this talk, guy does great job at explaining the event loop and js execution). This is why settimeout is sometimes used as an hack to run our code after call stack is empty (running currently functions ended the execution)

Collapse
 
firaskh1992 profile image
firas khateeb • Edited

hey Nikita,
Thanks for sharing such a great article,
one more thing I suggest to replace setTimeout with queueMicrotask, since then/catch/finally callbacks is a microTasks and not Tasks as setTimeout callback.

in such a case promises callback will be passed by JS engine into the microTask queue.

Collapse
 
sajjadalidev profile image
Sajjad Ali

Thanks for sharing
::)