Проблема в том, что, поскольку вы используете выражение, функция запускается при рендеринге, возвращая наблюдаемое.Когда значение наблюдаемой изменяется, она снова выполняет визуализацию той части представления, которая снова вызывает функцию, которая повторяется вечно.
В комментарии вы сказали:
На самом делеМне нужно несколько раз вызвать этот метод для разных URI извлечения на одной странице и связать с отдельными элементами HTML.
... и добавил этот пример к вопросу:
<div data-bind="text: fetchResults('some-url')"></div>
<div data-bind="text: fetchResults('some-different-url')"></div>
<div data-bind="text: fetchResults('another-url-altogether')"></div>
Это немного меняет дело.Я бы, вероятно, использовал пользовательскую привязку, а не функцию:
ko.bindingHandlers.textFetch = {
update(element, valueAccessor) {
const url = ko.unwrap(valueAccessor());
element.textContent = "0";
fetch(url)
.then((response) => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
})
.then((data) => {
element.textContent = data.length;
});
}
};
const viewModel = function() {
};
ko.applyBindings(new viewModel());
<div data-bind="textFetch: 'https://jsonplaceholder.typicode.com/photos'"></div>
<div data-bind="textFetch: 'https://jsonplaceholder.typicode.com/posts'"></div>
<div data-bind="textFetch: 'https://jsonplaceholder.typicode.com/comments'"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
Но если вы хотите, чтобы он был более похож на ваш текущий код, сохраняйте кэш наблюдаемых для URL:
const viewModel = function() {
const self = this;
// In a modern environment, `observables` could be a Map rather than an object
const observables = Object.create(null);
self.fetchResults = function(url) {
// Get the observable if we have it
let id = observables[url];
if (!id) {
// We don't, create and remember one, do the fetch
id = observables[url] = ko.observable(0);
fetch(url)
.then(function(response) {
return response.json();
})
.then(function(data) {
id(data.length);
})
}
return id;
};
};
ko.applyBindings(new viewModel());
<div data-bind="text: fetchResults('https://jsonplaceholder.typicode.com/photos')"></div>
<div data-bind="text: fetchResults('https://jsonplaceholder.typicode.com/posts')"></div>
<div data-bind="text: fetchResults('https://jsonplaceholder.typicode.com/comments')"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
Примечание: Вы не проверяете успешность обратного вызова fetch
.Вы не единственный, я бы даже сказал, что API fetch
плохо спроектирован, так как я вижу, что этот пистолет стреляет почти каждый день здесь, на SO.Я написал это в своем маленьком анемичном блоге, но в основном вам нужно добавить:
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
... в ваш обработчик выполнения.