0

I had a job interview yesterday, and I was given a coding challenge as the following:

// 3. Coding challenge.
// The goal is to make function1/function2 to work only when the constructor has finished its async operations.
// You CAN'T change the notifyUrls function. Imagine it's a 3th party library you don't have control on.

// CAN'T CHANGE THIS.
//===================
function notifyUrls(item, callback) {
    asyncOperation(item).then((res) => {
        callback(res);
    });
}
//===================

 const asyncOperation = () => { return new Promise((resolve, reject) => { setTimeout(() => { console.log('Timeout execute'); resolve(); }, 2000); }); };

const URL1 = 'http://www.somerestapi/get1';
const URL2 = 'http://www.somerestapi/get2';
const URL3 = 'http://www.somerestapi/get3';

class MyClass {

    constructor() {
        [URL1, URL2, URL3].forEach(item => {
            notifyUrls(item, () => { });
        });
    }

    myFunction1() {
        // Only start working when constructor finished notifying.
        // ...
        console.log('myFunction1');
    }

    myFunction2() {
        // Only start working when constructor finished notifying.
        // ...
        console.log('myFunction2');
    }
}

And here is what I did:

// CAN'T CHANGE THIS.
//===================
function notifyUrls(item, callback) {
    asyncOperation(item).then((res) => {
        callback(res);
    });
}
//===================

 const asyncOperation = () => { return new Promise((resolve, reject) => { setTimeout(() => { console.log('Timeout execute'); resolve(); }, 2000); }); };

const URL1 = 'http://www.somerestapi/get1';
const URL2 = 'http://www.somerestapi/get2';
const URL3 = 'http://www.somerestapi/get3';

class MyClass {

    constructor() {
        this.ready = Promise.all([URL1, URL2, URL3].map((url) => { this.myAsyncCall(url); }));
    }

    myAsyncCall(item) {
        return new Promise((resolve, reject) => {
            notifyUrls(item, (res) => { resolve(res); });
        });
    }

    async myFunction1() {
        if (await this.ready) {
            // Only start working when constructor finished notifying.
            // ...
            console.log('myFunction1');
        }
    }

    myFunction2() {
        // Only start working when constructor finished notifying.
        // ...
        console.log('myFunction2');
    }
}

(async () => {
    const myClass = new MyClass();
    await myClass.myFunction1();
})();

But the output is:

myFunction1
Timeout execute
Timeout execute
Timeout execute

What I want the output to be is:

Timeout execute
Timeout execute
Timeout execute
myFunction1

How can I work around it? Thanks.

Or Assayag
  • 5,662
  • 13
  • 57
  • 93
  • 1
    where is this "timeout execute" coming from? not the code you posted – Jaromanda X Jun 07 '21 at 08:01
  • Sorry, my bad. I updated the code. – Or Assayag Jun 07 '21 at 08:02
  • 1
    the problem is in `this.ready = Promise.all([URL1, URL2, URL3].map((url) => { this.myAsyncCall(url); }));` ... your mapping callback doesn't return anything – Jaromanda X Jun 07 '21 at 08:05
  • 1
    This is related [Asynchronous operations in a constructor](https://stackoverflow.com/questions/49905178/asynchronous-operations-in-constructor/49906064#49906064). My preferred mechanism would be to create a factory function that returns a promise that resolves to the newly constructed instance when all the asynchronous operations are done. This does not surface the new object until the asynchronous stuff is done. Use `Promise.all()` to track multiple asynchronous operations. Promisify them if they are not already promisified. – jfriend00 Jun 07 '21 at 08:06

1 Answers1

0

The problem is in

    this.ready = Promise.all([URL1, URL2, URL3].map((url) => { this.myAsyncCall(url); }));

Your map callback doesn't return anything

Either do

    this.ready = Promise.all([URL1, URL2, URL3].map((url) => { return this.myAsyncCall(url); }));

or

    this.ready = Promise.all([URL1, URL2, URL3].map((url) =>  this.myAsyncCall(url)));

Adding some coding tips

        notifyUrls(item, (res) => {
            resolve(res);
        });

Can simply be

        notifyUrls(item, resolve);

and

const asyncOperation = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve();
        }, 2000);
    });
};

is just

const asyncOperation = () => new Promise(resolve => setTimeout(resolve, 2000));
Jaromanda X
  • 53,868
  • 5
  • 73
  • 87