简体   繁体   English

淘汰赛JS-承诺递归

[英]Knockout JS - Promise recursion

I have a situation in which I have to update an Knockout observable bound to an HTML element and its value is being updated by an asynchronous operation (fetch from a server). 在这种情况下,我必须更新绑定到HTML元素的可观察的可观察对象,并且其值正在通过异步操作(从服务器中获取)进行更新。

I wrote the following sample code: 我编写了以下示例代码:

 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> 

It is creating an infinite recursive loop. 它正在创建一个无限的递归循环。

Is Knockout calling the function repeatedly because value is being updated inside an asynchronous function? 敲除是否在异步函数中更新值而重复调用该函数? What could be the root cause of this issue? 可能是此问题的根本原因是什么?

TIA! TIA!

Edit: 编辑:

I have multiple HTML elements on the page requesting different information to be bound to them, for example, 我在页面上有多个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>

That would mean I need to create observable inside the fetchResults function which I did (please correct me if my understanding is amiss :)) 那意味着我需要在我执行的fetchResults函数内部创建可观察的(如果我的理解fetchResults ,请更正我:)

The problem is that since you're using an expression, the function gets run on render, returning an observable. 问题在于,由于您正在使用表达式,因此该函数将在render上运行,并返回一个可观察的对象。 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. 实际上,我需要针对同一页面中的不同提取URI多次调用此方法,并将其绑定到单独的HTML元素。

...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: 但是,如果要使其与当前代码更相似,请保留可观察的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> 


Side note: You're not checking for success in your fetch callback. 旁注:您不会在fetch回调中检查是否成功。 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. 您不是唯一的一个,我会说fetch API的设计很差,因为我看到这支步枪几乎每天都在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. ...在您的履行处理程序中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM