如何防止JavaScript中异步init方法的并行执行?
我想在页面加载后立即在JavaScript中预加载搜索索引,这样用户就不必等待它的生成。我知道JavaScript是单线程的,通常不需要锁,但我不确定以下代码是否足够:如何防止JavaScript中异步init方法的并行执行?,javascript,async-await,Javascript,Async Await,我想在页面加载后立即在JavaScript中预加载搜索索引,这样用户就不必等待它的生成。我知道JavaScript是单线程的,通常不需要锁,但我不确定以下代码是否足够: class Index { constructor() { this.index = null; } async init() { this.index = await queryDatabase(); } async search(input) { if(!this.
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?如果不是,如何以最佳方式防止这种情况发生?init()
是async
,它返回一个承诺,您可以在查询数据库后添加事件侦听器:
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())
});
});
与@Taki(upvote-his)相同,但100%使用异步/等待语法
IIFE语法:
以上两个答案是“恰到好处的”
我还想提出一个意见,即我不推荐现在出现在search()
方法中的代码位:
if(!this.index) {await this.init();}
相反,“在您安装事件监听器之前,请确保它已准备好使用,这样将尝试使用它。”您也可以在构造函数中初始化它
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
并像let index=wait new index
那样实例化它,但它没有得到广泛使用,可能会引起混淆。(另一种方法是工厂法)
**还要注意,您的实现并不安全,您可以轻松地多次调用
search
,而您(错误地)多次调用init
。(这是有效的用例,如Promise.All(搜索(A),搜索(B))
)我想知道您是否真的需要这个类
基础结构。如果索引
就这么简单,那么最好只创建搜索
函数,访问查询数据库
返回的承诺。这个版本可以做到这一点,并用一个进度条演示,该进度条模拟queryDatabase
返回的时间。在返回之前,您可以单击搜索按钮一次或多次,并且仅当数据可用时才会显示输出
queryDatabase
使用对象{foo:42,bar:99,baz:7}
伪造,返回“foo”
,“bar”
和“baz”
的值
const search=((索引)=>
异步(输入)=>(等待索引)。查询(输入)
)(queryDatabase())
myButton.addEventListener('click',async()=>{
日志(等待搜索(getTerm()))
})
.bar{border:1px solid}666;高度:15px;宽度:100%;}.bar.in{animation:fill 5s linear 1;高度:100%;背景色:蓝色;}@keyframes fill{0%{width:0%;}100%{width:100%;}选择,按钮{margin left:1em;}
搜索词foobarbazSearch输出:
const queryDatabase=()=>newpromise((res)=>setTimeout(()=>res({query:key=>({foo:42,bar:99,baz:7})[key]}),5000))
常量myButton=document.getElementById('myButton')
const term=document.getElementById('term'))
const output=document.getElementById('output')
const getTerm=()=>term.options[term.selectedIndex].value
常量日志=(msg)=>output.innerHTML+='\n'+msg
您可以将myButton
传递到init()
中,并仅在查询返回后添加事件侦听器。this.index=wait queryDatabase();myButton.addEventListener(…)代码>myButton.addEventListener(“单击”,console.log(index.search())代码>是一个有问题的问题。您将console.log的返回值绑定为事件侦听器。这意味着,无论当前登录到控制台的是什么,都与是否有人单击了该按钮无关。@Yoshi和事实index.search
是async
,我相信这会让情况变得更糟。(除非OP真的想记录Promise
)@yoshi:对不起,我在将真实代码简化为示例时犯了一个错误,我只想打印搜索结果。这应该是一个异步箭头函数,它等待结果,然后打印结果。
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);
}
}