如何防止JavaScript中异步init方法的并行执行?

如何防止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.

我想在页面加载后立即在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?如果不是,如何以最佳方式防止这种情况发生?

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);
  }
}