javascript - 淘汰赛 JS - 承诺递归
问题描述
我有一种情况,我必须更新绑定到 HTML 元素的 Knockout 可观察对象,并且它的值正在通过异步操作(从服务器获取)进行更新。
我编写了以下示例代码:
const viewModel = function() {
const self = this;
self.fetchResults = function() {
const id = ko.observable(0);
fetch("https://jsonplaceholder.typicode.com/photos")
.then(function(response) {
return response.json();
})
.then(function(data) {
id(data.length);
console.log(id());
})
return id;
};
};
ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div data-bind="text: fetchResults()"></div>
它正在创建一个无限递归循环。
Knockout 是否因为异步函数内部正在更新值而重复调用该函数?这个问题的根本原因可能是什么?
蒂亚!
编辑:
我在页面上有多个 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>
这意味着我需要在fetchResults
我所做的函数中创建 observable(如果我的理解有误,请纠正我 :))
解决方案
The problem is that since you're using an expression, the function gets run on render, returning an observable. When the observable's value changes, it makes that part of the view render again, which calls the function again, which cycles forever.
In a comment you've said:
Actually I need to call this method multiple times for different fetch URIs in the same page, and bind to separate HTML elements.
...and added this example to the question:
<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>
That does change things a bit. I'd probably use a custom binding rather than a function:
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>
But if you want to keep it more similar to your current code, keep a cache of observables for urls:
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>
Side note: You're not checking for success in your fetch
callback. You're not the only one, I'd go so far as to say the fetch
API is poorly-designed as I see this footgun being fired just about every day here on SO. I've written it up on my anemic little blog, but basically, you need to add:
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
...in your fulfillment handler.
推荐阅读
- spring - Spring TransactionManager 行为与 Spring Data 和 JpaRepository
- javascript - 使用 WP 辅助功能插件的 Wordpress 验证错误
- flutter - 使用 photo_view 包来适应像 BoxFit.cover 这样的图像
- sql - 动态调整最近 5 天的生产
- python-3.x - 如何防止 GCP Vertex 从我的 docker run 中丢失日志?
- angular - 如何使用 angular、firebase 实现无密码登录身份验证并将电子邮件存储在 mongodb-stitch 中?
- javascript - 如何将对象传递给formick表单
- flutter - Firebase rules/secure in collectionGroup use if state
- windows - Delphi 库例程 IsUNCRooted 和 IsUNCPath 有什么区别?
- python - 用正则表达式和python替换所有html href中的下划线