Erez
Erez

Reputation: 205

Simple Promise and Then implementation

Recently I was shown a piece of code that were asked during a full-stack developer interview. It involved creating a Promise, in which the candidate should implement, passing it a resolve function, and chaining 2 then's.

I tried implementing the Promise very naively only to make the code work. Created a ctor that accepts a resolver func, Created a Then function that accepts a callback and returns a Promise, and simply calls the callback on the resolver function.

class MyPromise {

    constructor(resolver) {
        this.resolver = resolver;
    }

    then(callback) {
        const result = new MyPromise(callback);
        this.resolver(callback);

        return result;
    }
}

promise = new MyPromise(
    (result) => {
        setTimeout(result(2), 500);
    });
promise.then(result => {
    console.log(result);
    return 2 * result;
}).then(result => console.log(result));

The expected result is 2,4 - just like operating with real Promise. But i'm getting 2,2. I'm having trouble figuring out how to get the return value for the first "then" and passing it along.

Upvotes: 8

Views: 1986

Answers (5)

ChrisM
ChrisM

Reputation: 1672

There are a few problems with your original code. Notably you are only executing the constructor argument upon call of the then method and you aren't actually chaining the outputs of the 'then' callbacks.

Here is a very (very!) basic promise implementation based upon adapting your example. It will also work for cases where 'then' is called after the promise is resolved (but not if 'then' is already called - multiple then blocks is not supported).

class MyPromise {
    constructor(resolver) {
        let thisPromise = this;

        let resolveFn = function(value){
            thisPromise.value = value; 
            thisPromise.resolved = true;
            if(typeof thisPromise.thenResolve === "function"){
                thisPromise.thenResolve();
            }
            
        }
        if (typeof resolver === "function") {
            resolver(resolveFn);
        }
    }
    then(callback) {
        let thisPromise = this;
        thisPromise.thenFn = callback;

        return new MyPromise((resolve) =>{
            thisPromise.thenResolve = () => {
                thisPromise.value = thisPromise.thenFn(thisPromise.value);
                resolve(thisPromise.value);
            }
            //automatically resolve our intermediary promise if 
            //the parent promise is already resolved
            if(thisPromise.resolved){
                thisPromise.thenResolve();
            }
        });

        
    }
};

//test code

console.log("Waiting for Godot...");

promise = new MyPromise((resolve) =>{
    setTimeout(()=>{
        resolve(2)
    },500);
});


promise.then((result) => {
    console.log(result);
    return 2 * result;
}).then((result) => {
    console.log(result);
    return 2 * result;
}).then((result) => {
    console.log(result)
}); 

Upvotes: 0

Manuel Richarz
Manuel Richarz

Reputation: 1926

How about very simple:

const SimplePromise = function(cb) {
  cb(
    data =>
      (this.data = data) &&
      (this.thenCb || []).forEach(chain => (this.data = chain(this.data))),
    error =>
      (this.error = error) &&
      (this.catchCb || []).forEach(chain => (this.error = chain(this.error)))
  );
  this.then = thenCb =>
    (this.thenCb = [...(this.thenCb || []), thenCb]) && this;
  this.catch = catchCb =>
    (this.catchCb = [...(this.catchCb || []), catchCb]) && this;
};

Example Here: https://codesandbox.io/s/0q1qr8mpxn

Upvotes: 2

arizafar
arizafar

Reputation: 3122

Here's the shortened code for creating a promise class,

class MyPromise {
  constructor(executor) {
    this.callbacks = [];

    const resolve = res => {
      for (const { callback } of this.callbacks) {
        callback(res);
      }
    };

    executor(resolve);
  }

  then(callback) {
    return new MyPromise((resolve) => {
      const done = res => {
        resolve(callback(res));
      };
      this.callbacks.push({ callback: done });
    });
  }
}


promise = new MyPromise((resolve) => {
  setTimeout(() => resolve(2), 1000);
});

promise.then(result => {
  console.log(result);
  return 2 * result;
}).then(result => console.log(result));

Upvotes: 3

trincot
trincot

Reputation: 350147

Your question has some issues:

  • The r2 variable is nowhere defined. I will assume result was intended.
  • The setTimeout is doing nothing useful, since you execute result(2) immediately. I will assume setTimeout(() => result(2), 500) was intended.

If the code was really given like that in the interview, then it would be your job to point out these two issues before doing anything else.

One issue with your attempt is that the promise returned by the then method (i.e. result) is never resolved. You need to resolve it as soon as the this promise is resolved, with the value returned by the then callback.

Also, the promise constructor argument is a function that should be executed immediately.

In the following solution, several simplifications are made compared to a correct Promise behaviour.

  • It does not call the then callbacks asynchronously;
  • It does not support multiple then calls on the same promise;
  • It does not provide the rejection path;
  • It does not prevent a promise from resolving twice with a different value;
  • It does not deal with the special case where a then callback returns a promise

console.log("Wait for it...");

class MyPromise {
    constructor(executor) {
        executor(result => this.resolve(result));
    }
    resolve(value) {
        this.value = value;
        this.broadcast();
    }
    then(onFulfilled) {
        const promise = new MyPromise(() => null);
        this.onFulfilled = onFulfilled;
        this.resolver = (result) => promise.resolve(result);
        this.broadcast();
        return promise;
    }
    broadcast() {
        if (this.onFulfilled && "value" in this) this.resolver(this.onFulfilled(this.value)); 
    }
};

// Code provided by interviewer, including two corrections
promise = new MyPromise(
    (result) => {
        setTimeout(()=>result(2), 500); // don't execute result(2) immediately
    });
promise.then(result => {
    console.log(result); // Changed r2 to result.
    return 2 * result;
}).then(result => console.log(result));

Note the 500ms delay in the output, which is what should be expected from the (corrected) setTimeout code.

I posted a full Promises/A+ compliant promise implementation with comments in this answer

Upvotes: 2

antanta
antanta

Reputation: 740

Implying that this r2 is actually the result parameter. The problem with your code is that you do not retrieve the result from the result(2). The first "then" gets executed, prints 2, return 4, but this 4 is just wasted. I wrote some code with synchronous function only to demonstrate what to do if you want to get this 2,4 output:

class MyPromise {
    constructor(resolver) {
        this.resolver = resolver;
    }

    then(callback) {
          var res = callback(this.resolver());
        var result = new MyPromise(() => { return res; });

        return result;
    }
}

let promise = new MyPromise(
    () => {
        return 2;
    });

promise
.then(result => {
    console.log(result);
    return 2 * result;
})
.then(result => {
        console.log(result);
});

If you want the resolver to be somewhat async you should use Promises (because return value from function executed inside setTimeout can be retrieved, see here.

If you are not allowed to use these built-in Promises, you can write them yourself with some dummy deferred object and await them (setInterval) to resolve (should be basically the same logic).

Upvotes: 0

Related Questions