我想在页面加载后立即在JavaScript中预加载搜索索引,这样用户可能不必等待构建它。我知道JavaScript是单线程的,通常不需要锁,但是我不确定以下代码是否足够:
class Index
{
constructor()
{
this.index = null;
}
async init()
{
this.index = await queryDatabase();
}
async search(input)
{
if(!this.index) {await this.init();}
return this.index.query(input);
}
}
Index index = new index();
index.init(); // preload
myButton.addEventListener("click",console.log(index.search()));
是否会发生用户在init()完成之前单击myButton并两次调用init的情况?如果没有,我该如何最好地防止这种情况发生?
答案 0 :(得分:5)
init()
是async
,它返回一个Promise,您可以在查询数据库后添加事件侦听器:
class Index {
constructor() {
this.index = null;
}
async init() {
this.index = await queryDatabase();
}
async search(input) {
if (!this.index) {
await this.init();
}
return this.index.query(input);
}
}
Index index = new index();
index.init().then(() => {
// add the event listener after the init
myButton.addEventListener("click", function(){
console.log(index.search())
});
});
答案 1 :(得分:3)
与@Taki相同(赞成),但使用异步/等待语法100%。
IIFE语法:https://jack.ofspades.com/es6-iife-with-fat-arrow-functions/
class Index {
constructor() {
this.index = null;
}
async init() {
this.index = await queryDatabase();
}
async search(input) {
// should never happend
if (this.index === null) {
await this.init();
}
return this.index.query(input);
}
}
const index = new index();
(async() => {
await index.init();
// add the event listener after the init
myButton.addEventListener('click', console.log(index.search()));
})();
答案 2 :(得分:2)
您也可以在构造函数中将其初始化。
class Index
{
constructor(){this.index = queryDatabase();}
async search(input){
return (await this.index).query(input);
}
}
或简单地将search
更改为普通功能,并假设用户会调用init()
并等待它。
class Index
{
constructor(){this.index = null;}
async init(){this.index = await queryDatabase();}
search(input)
{
//just assume the `init()` is called.
return this.index.query(input);
}
}
*您实际上可以使构造函数返回一个Promise<Index>
并像let index = await new Index
一样实例化它,但是它没有被广泛使用,并且可能引起混乱。 (一种替代方法是工厂方法)
**还请注意,您的实现并不安全,您可以轻松地多次调用search
,而您(错误地)多次调用init
。 (这是有效的用例,例如Promise.All(search(A),search(B))
)
答案 3 :(得分:1)
以上两个答案是“现货”。
我还想提出一个观点,即我不推荐使用search()
方法中现在出现的一些代码:
if(!this.index) {await this.init();}
相反,“在安装尝试使用它的事件侦听器等之前,只需确保已准备好使用 即可。”
答案 4 :(得分:1)
我想知道您是否真的需要ComponentDidMount(){
this.apiReq();
}
apiReq(){
request code here....
}
基础架构。如果class
就这么简单,那么最好创建Index
函数并访问search
返回的Promise。该版本可以做到这一点,并通过进度条进行演示,该进度条模拟queryDatabase
返回之前的时间。您可以在返回之前单击一次或多次搜索按钮,并且只有在数据可用时才会显示输出。
queryDatabase
被对象queryDatabase
伪造,返回{foo: 42, bar: 99, baz: 7}
,"foo"
和"bar"
的值。
"baz"
const search = ((index) =>
async (input) => (await index) .query (input)
) (queryDatabase ())
myButton .addEventListener ('click', async () => {
log (await search (getTerm ()))
})
.bar {border: 1px solid #666; height: 15px; width: 100%;} .bar .in {animation: fill 5s linear 1; height: 100%; background-color: blue;} @keyframes fill {0% {width: 0%;} 100% {width: 100%;}} select, button {margin-left: 1em;}
您可以使用以下类似方式更明确地说明这一点:
<p><label>Search Term <select id="term"><option>foo</option><option>bar</option><option>baz</option></select></label><button id="myButton">Search</button></p><div class="bar"><div class="in"></div></div><pre id="output">Output:</pre>
<script>
const queryDatabase = () => new Promise ((res) => setTimeout(() => res ({query: key => ({foo: 42, bar: 99, baz: 7})[key]}), 5000))
const myButton = document.getElementById ('myButton')
const term = document.getElementById ('term')
const output = document.getElementById ('output')
const getTerm = () => term.options[term.selectedIndex].value
const log = (msg) => output.innerHTML += '\n' + msg
</script>
但是无论哪种方式,这似乎都比一个类简单,该类大概只能实例化一次。