假设我在spanish.json
:
[
{"word": "casa", "translation": "house"},
{"word": "coche", "translation": "car"},
{"word": "calle", "translation": "street"}
]
我有一个加载它的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
}
}
我可以实例化,并用它来查找'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>
到目前为止一切顺利。但是,假设我想要继承Dictionary
并添加一个计算所有单词的方法并添加
数到页面。 (伙计,我需要一些投资者。)
所以,我得到了另一轮资助并实施CountingDictionary
:
class CountingDictionary extends Dictionary {
constructor(url){
super(url)
}
countEntries(){
return this.entries.length
}
}
新的单页应用:
<!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>
加载此页面时,h1
会填充0
。
我知道我的问题是什么,我不知道如何解决它。
问题是fetch
调用返回Promise
,
并且.entries
属性仅填充数据
一旦Promise返回,就从URL开始。直到那时,
.entries
仍为空。
如何让.countEntries
等待获取承诺解析?
还是有更好的方法来完成我想要的东西吗?
答案 0 :(得分:4)
问题是
fetch
调用返回Promise
, 并且.entries
属性仅填充数据 一旦Promise返回,就从URL开始。直到那时,.entries
仍为空。
您需要将entries
作为承诺。这样,你的所有方法都必须返回promises,但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;
});
});
还是有更好的方法来完成我想要的东西吗?
是。看看Is it bad practice to have a constructor function return a 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(…);
});
正如您所看到的,与包含promise的实例相比,此appraoch需要更少的整体嵌套。对实例的承诺也是更好的可组合,例如当您需要在安装侦听器并显示输出之前等待domready时,您将能够获得DOM的承诺,并且可以等待使用Promise.all
。
答案 1 :(得分:0)
您必须将fetch()
调用的结果分配给某个变量,例如:
initialize(){
this.promise = fetch(this.url)
.then(response => response.json())
.then(entries => this.entries = entries)
}
然后你可以调用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}`;
})
答案 2 :(得分:0)
一个简单的解决方案:在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) { /* ... */ }
}
然后,在构建完对象后,只需致电.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
});
作为一个额外的优点,您只需使用.catch
来检测加载过程中发生的错误,例如网络错误或未捕获的异常。