Функция рефакторинга и лучшая цепочка обещаний

#javascript #promise

#javascript #обещание

Вопрос:

В настоящее время у меня есть эта функция, и она работает:

 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);  

.. но мне было интересно, как я могу улучшить это, чтобы мне не приходилось связывать обещания каждый раз, когда мне приходится проверять наличие вложенного объекта?

Комментарии:

1. вы имеете в виду, как waitForObjectProperty(window, "google") .then(object => waitForObjectProperty(object, "maps")) .then(object => waitForObjectProperty(object, "places")) .then(object =>console.log('places object:', google.maps.places));

2. Можете ли вы использовать синтаксис ES8? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference /…

3. async/await намного проще

Ответ №1:

 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);
  });
  

Комментарии:

1. Разве мне не нужно bind передавать дополнительные параметры?

2. @3zzy Нет. Этот метод называется currying , ведет себя вроде как bind , но по-другому.

Ответ №2:

Преимущества метода, показанного ниже, в том, что он использует одну строку кода и, что, возможно, более важно, «путь» может быть создан динамически

Краткое объяснение

Путь (после «корня» — window в вашем случае) представляет собой строку в точечной записи, поэтому сначала разделите эту строку на части

С полученным массивом используйте метод array.reduce, чтобы создать цепочку обещаний — «начальное значение» для array.reduce является Promise.resolve(root) , и каждое последующее обещание преобразуется (как в вашем коде) в «созданный» объект

Конечно, «вспомогательный» код больше, но конечный результат так же прост, как

 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);  

В современном javascript вы можете написать это следующим образом — (не уверен, почему вы не можете использовать современный javascript (согласно комментарию в другом ответе относительно функций со стрелками))

 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);  

Комментарии:

1. Не могу использовать современный JS, потому что IE11 и нет babel 🙂 Но огромное спасибо за ответ, как раз то, что я искал!

Ответ №3:

 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);  

Комментарии:

1. Это то, что я делаю правильно? Просто не могу использовать функции со стрелками.

2. нет, у вас вложенный .тогда @3zzy — это . затем цепочка — но это не то, что вы имели в виду в вопросе, не так ли