es6 classes:父类中的fetch,参考子类中的resolved fetch
Posted
技术标签:
【中文标题】es6 classes:父类中的fetch,参考子类中的resolved fetch【英文标题】:es6 classes: Fetch in the parent class, refer to resolved fetch in child class 【发布时间】:2017-01-16 14:57:07 【问题描述】:假设我在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
等待获取承诺解决?
或者有没有更好的方法来完全实现我想要的?
【问题讨论】:
【参考方案1】:问题是
fetch
调用返回一个Promise
, 并且.entries
属性仅填充了数据 一旦 Promise 返回,从 URL 中获取。直到那时,.entries
仍为空。
您需要做出entries
的承诺。这样一来,您的所有方法都必须返回 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;
);
);
或者有没有更好的方法来完全实现我想要的?
是的。看看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 的实例相比,这种方法需要更少的整体嵌套。实例的承诺也可以更好地组合,例如当您需要在安装侦听器和显示输出之前等待 domready 时,您将能够获得对 DOM 的承诺,并且可以使用 Promise.all
等待两者。
【讨论】:
感谢您的评论。在我看来,这种方法的问题在于,您最终仍然会得到一个加载方法,该方法本质上是类外部的。虽然将 .load 放在类的静态方法中进行了一些封装,但调用该方法的模式与仅在全局命名空间中运行 fetch(url),然后使用该数据实例化类仍然没有什么不同. 是的,有点不同,但这是正确的做法 :-) 您总是需要“在外部”等待。load
方法必须是静态的有什么原因吗?
@pat 它为一个实例返回一个promise,它不能在一个还不存在的实例上调用。【参考方案2】:
您必须将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`;
)
【讨论】:
这和Frxstrem的反应很相似,所以也有同样的缺点。【参考方案3】:一个简单的解决方案:在你做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
来检测加载过程中发生的错误,例如网络错误或未捕获的异常。
【讨论】:
ready()
方法的作用是什么,什么时候可以直接访问promiseReady
?
@Gothdo:老实说,这主要是个人约定,但想法是仅通过 getter 公开(因此不能从外部修改),我认为拥有一个返回的函数更有意义作为“动作”的承诺,(即“等待此类准备就绪”)而不是直接访问。
这让我想起了这种方法:quickleft.com/blog/leveraging-deferreds-in-backbonejs。虽然从 fetch 调用在类内部的意义上说是“自包含的”(并且这里 Dictionary 因此使用 url 作为构造函数参数),但缺点是任何使用类实例的客户端都必须参考“永远”的 .ready() 方法……在我看来,这使得 .initialize() 似乎并没有真正初始化……以上是关于es6 classes:父类中的fetch,参考子类中的resolved fetch的主要内容,如果未能解决你的问题,请参考以下文章