eozzy
eozzy

Reputation: 68660

Refactoring function and better promise chaining

I currently have this function and it works:

function waitForObjectProperty(object, property) {
    return new Promise(function(resolve, reject) {
        Object.defineProperty(Object.prototype, property, {
            configurable: true,
            set: function(value) {
                Object.defineProperty(object, property, {
                    value: value,
                });
                resolve(object[property]);
            },
        });
    });
}

waitForObjectProperty(window, "google").then(function(object) {
    waitForObjectProperty(object, "maps").then(function(object) {
        waitForObjectProperty(object, "places").then(function(object) {
            console.log('places object:', google.maps.places);
        });
    });
});


setTimeout(function(){ window.google = {} }, 1000);
setTimeout(function(){ window.google.maps = {} }, 2000);
setTimeout(function(){ window.google.maps.places = {} }, 3000);

.. but I was wondering how can I could improve it so I don't have to chain promises every time I have to check for a nested object?

Upvotes: 1

Views: 64

Answers (3)

Jaromanda X
Jaromanda X

Reputation: 1

The benefits of the method shown below is that its usage is a single line of code and, maybe more importantly, the "path" can be dynamically created

A brief explanation

The path (after the "root" - window in your case) is a string in dot notation, so first split that string into parts

With the resulting array, use the array.reduce method to make the chain of promises - the "seed" to array.reduce is Promise.resolve(root) and each subsequent promise resolves (as in your code) to the "created" object

Sure the "supporting" code is bigger, but the end result is as simple as

waitForNestedObject(window, 'some.very.long.path.that.you.can.even.build.dynamically.if.you.want');

function waitForNestedObject(root, objectPath) {
    var waitForObjectProperty = function (object, property) {
        return new Promise(function(resolve, reject) {
            Object.defineProperty(Object.prototype, property, {
                configurable: true,
                set: function(value) {
                    Object.defineProperty(object, property, {
                        value: value,
                    });
                    resolve(object[property]);
                },
            });
        });
    }
    var steps = objectPath.split('.');
    return steps.reduce(function(p, step) {
        return p.then(function(object) {
            return waitForObjectProperty(object, step);
        });
    }, Promise.resolve(root));
}
//
// use is simple now
//
waitForNestedObject(window, 'google.maps.places')
.then(function(object) {
    console.log(object);
});

setTimeout(function(){ window.google = {} }, 1000);
setTimeout(function(){ window.google.maps = {} }, 2000);
setTimeout(function(){ window.google.maps.places = {hello:'world'} }, 3000);

In modern javascript you can write it like the following - (not sure why you can't use modern javascript (as per comment in another answer regarding arrow functions))

const waitForNestedObject = (root, objectPath) => {
    const waitForObjectProperty = (object, property) => new Promise((resolve, reject) => Object.defineProperty(Object.prototype, property, {
        configurable: true,
        set: function(value) {
            Object.defineProperty(object, property, {
                value: value,
            });
            resolve(object[property]);
        },
    }));
    return objectPath.split('.').reduce((p, step) => p.then(object => waitForObjectProperty(object, step)), Promise.resolve(root));
};
//
// usage remains the same
waitForNestedObject(window, 'google.maps.places')
.then(object => console.log(object));

setTimeout(function(){ window.google = {} }, 1000);
setTimeout(function(){ window.google.maps = {} }, 2000);
setTimeout(function(){ window.google.maps.places = {hello:'world'} }, 3000);

Upvotes: 1

hackape
hackape

Reputation: 19947

const tapProp = property => object => waitForObjectProperty(object, property);

Promise.resolve(window)
  .then(tapProp("google"))
  .then(tapProp("maps"))
  .then(tapProp("places"))
  .then(function(object) {
    console.log('places object:', google.maps.places);
  });

Upvotes: 2

waynelpu
waynelpu

Reputation: 435

function waitForObjectProperty(object, property) {
    return new Promise(function(resolve, reject) {
        Object.defineProperty(Object.prototype, property, {
            configurable: true,
            set: function(value) {
                Object.defineProperty(object, property, {
                    value: value,
                });
                resolve(object[property]);
            },
        });
    });
}
// you can just return promise and then chainning
waitForObjectProperty(window, "google")
    .then(object => waitForObjectProperty(object, "maps"))
    .then(object => waitForObjectProperty(object, "places"))
    .then(object => console.log('places object:', google.maps.places));


setTimeout(function(){ window.google = {} }, 1000);
setTimeout(function(){ window.google.maps = {} }, 2000);
setTimeout(function(){ window.google.maps.places = {} }, 3000);

Upvotes: 0

Related Questions