Reputation: 68660
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
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
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
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