简体   繁体   English

es6类:在父类中获取,请参阅子类中的已解析获取

[英]es6 classes: Fetch in the parent class, refer to resolved fetch in child class

Suppose I have this data in spanish.json : 假设我在spanish.json有此数据:

[
   {"word": "casa", "translation": "house"},
   {"word": "coche", "translation": "car"},
   {"word": "calle", "translation": "street"}
]

And I have a Dictionary class that loads it and adds a search method: 我有一个Dictionary类来加载它并添加一个搜索方法:

// Dictionary.js
class Dictionary {
  constructor(url){
    this.url = url;
    this.entries = []; // we’ll fill this with a dictionary
    this.initialize();
  }

  initialize(){
    fetch(this.url)
      .then(response => response.json())
      .then(entries => this.entries = entries)
  }

  find(query){
    return this.entries.filter(entry => 
      entry.word == query)[0].translation
  }
}

And I can instantiate that, and use it to look up 'calle' with this little single-page app: 我可以实例化它,并使用它通过这个小单页应用程序来查找“ calle”:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>spanish dictionary</title>
</head>
<body>

<p><input placeholder="Search for a Spanish word" type="">  
<p><output></output>

<script src=Dictionary.js></script>
<script>

  let es2en = new Dictionary('spanish.json')
  console.log(es2en.find('calle')) // 'street'

  input.addEventListener('submit', ev => {
    ev.preventDefault();
    let translation = dictionary.find(ev.target.value);
    output.innerHTML = translation;
  })

</script>


</body>
</html>

So far so good. 到现在为止还挺好。 But, let's say I want to subclass Dictionary and add a method that counts all the words and adds that count to the page. 但是,假设我想对Dictionary进行子类化,并添加一个对所有单词进行计数并将其添加到页面的方法。 (Man, I need some investors.) (伙计,我需要一些投资者。)

So, I get another round of funding and implement CountingDictionary : 因此,我获得了另一轮资金并实施了CountingDictionary

class CountingDictionary extends Dictionary {
  constructor(url){
    super(url)
  }

  countEntries(){
    return this.entries.length
  }
}

The new single page app: 新的单页应用程序:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Counting Spanish Dictionary</title>
</head>
<body>

<p><input placeholder="Search for a Spanish word" type="">  
<p><output></output>

<script src=Dictionary.js></script>
<script>


  let 
    es2en = new CountingDictionary('spanish.json'),
    h1 = document.querySelector('h1'),
    input = document.querySelector('input'),
    output = document.querySelector('output');

  h1.innerHTML = es2en.countEntries();

  input.addEventListener('input', ev => {
    ev.preventDefault();
    let translation = es2en.find(ev.target.value);
    if(translation)
      output.innerHTML = `${translation}`;
  })

</script>

</body>
</html>

When this page loads, the h1 gets populated with 0 . 加载此页面时, h1填充为0

I know what my problem is, I just don't how to fix it. 我知道我的问题是什么,我只是不知道如何解决。

The problem is that the fetch call returns a Promise , and the .entries property is only populated with the data from the URL once that Promise has returned. 问题在于, fetch调用返回Promise ,并且只有Promise返回后, .entries属性才会使用URL中的数据进行填充。 Until then, .entries remains empty. 在此之前, .entries保持为空。

How can I make .countEntries wait for the fetch promise to resolve? 如何让.countEntries等待获取承诺解决?

Or is there a better way entirely to achieve what I want here? 还是有一种更好的方法可以完全实现我在这里想要的?

The problem is that the fetch call returns a Promise , and the .entries property is only populated with the data from the URL once that Promise has returned. 问题在于, fetch调用返回Promise ,并且只有Promise返回后, .entries属性才会使用URL中的数据进行填充。 Until then, .entries remains empty. 在此之前, .entries保持为空。

You would need to make entries a promise. 您需要使entries成为一个承诺。 That way, all of your methods had to return promises, but the Dictionary instance is immediately usable. 这样,您的所有方法都必须返回Promise,但是Dictionary实例可以立即使用。

class Dictionary {
  constructor(url) {
    this.entriesPromise = fetch(url)
      .then(response => response.json())
  }
  find(query) {
    return this.entriesPromise.then(entries => {
       var entry = entries.find(e => e.word == query);
       return entry && entry.translation;
    });
  }
}
class CountingDictionary extends Dictionary {
  countEntries() {
    return this.entriesPromise.then(entries => entries.length);
  }
}

let es2en = new CountingDictionary('spanish.json'),
    h1 = document.querySelector('h1'),
    input = document.querySelector('input'),
    output = document.querySelector('output');

es2en.countEntries().then(len => {
  fh1.innerHTML = len;
});
input.addEventListener(ev => {
  ev.preventDefault();
  es2en.find(ev.target.value).then(translation => {
    if (translation)
      output.innerHTML = translation;
  });
});

Or is there a better way entirely to achieve what I want here? 还是有一种更好的方法可以完全实现我在这里想要的?

Yes. 是。 Have a look at Is it bad practice to have a constructor function return a Promise? 看看让构造函数返回Promise是不好的做法吗? .

class Dictionary {
  constructor(entries) {
    this.entries = entries;
  }  
  static load(url) {
    return fetch(url)
      .then(response => response.json())
      .then(entries => new this(entries));
  }

  find(query) {
    var entry = this.entries.find(e => e.word == query);
    return entry && entry.translation;
  }
}
class CountingDictionary extends Dictionary {
  countEntries() {
    return this.entries.length;
  }
}

let es2enPromise = CountingDictionary.load('spanish.json'),
    h1 = document.querySelector('h1'),
    input = document.querySelector('input'),
    output = document.querySelector('output');

es2enPromise.then(es2en => {
  fh1.innerHTML = es2en.countEntries();
  input.addEventListener(…);
});

As you can see, this appraoch requires less overall nesting compared to an instance that contains promises. 如您所见,与包含Promise的实例相比,此方案需要较少的整体嵌套。 Also a promise for the instance is better composable, eg when you would need to wait for domready before installing the listeners and showing output you would be able to get a promise for the DOM and could wait for both using Promise.all . 同样,该实例的Promise可以更好地组合,例如,当您需要在安装侦听器和显示输出之前等待domready时,您将能够获得DOM的Promise.all并且可以使用Promise.all等待两者。

You have to assign the result of the fetch() call to some variable, for example: 您必须将fetch()调用的结果分配给某个变量,例如:

initialize(){
  this.promise = fetch(this.url)
    .then(response => response.json())
    .then(entries => this.entries = entries)
}

Then you can call the then() method on it: 然后,可以在其上调用then()方法:

let es2en = new CountingDictionary('spanish.json'),
    h1 = document.querySelector('h1'),
    input = document.querySelector('input'),
    output = document.querySelector('output');

es2en.promise.then(() => h1.innerHTML = es2en.countEntries())

input.addEventListener('input', ev => {
  ev.preventDefault();
  let translation = es2en.find(ev.target.value);
  if(translation)
    output.innerHTML = `${translation}`;
})

A simple solution: Keep the promise after you do fetch() , then add a ready() method that allows you to wait until the class has initialized completely: 一个简单的解决方案:在执行fetch()之后遵守诺言,然后添加一个ready()方法,使您可以等待类完全初始化为止:

class Dictionary {
  constructor(url){
    /* ... */

    // store the promise from initialize() [see below]
    // in an internal variable
    this.promiseReady = this.initialize();
  }

  ready() {
    return this.promiseReady;
  }

  initialize() {
    // let initialize return the promise from fetch
    // so we know when it's completed
    return fetch(this.url)
      .then(response => response.json())
      .then(entries => this.entries = entries)
  }

  find(query) { /* ... */ }
}

Then you just call .ready() after you've constructed your object, and you'll know when it's loaded: 然后,在构造对象之后,只需调用.ready() ,就会知道何时加载该对象:

let es2en = new CountingDictionary('spanish.json')
es2en.ready()
  .then(() => {
    // we're loaded and ready
    h1.innerHTML = es2en.countEntries();
  })
  .catch((error) => {
    // whoops, something went wrong
  });

As a little extra advantage, you can just use .catch to detect errors that occur during loading, eg, network errors or uncaught exceptions. 另外,您还可以使用.catch来检测加载期间发生的错误,例如网络错误或未捕获的异常。

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

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