问题

有一个主函数同步调用一个异步函数,这个异步函数要等待用户输入后返回给主函数用户输入的结果。在用户输入前,要怎么阻塞线程,待用户输入完毕继续执行?

用 while 阻塞线程?

No

线程会永远阻塞在 while 上,外部调用都不会产生作用。因为外部那个函数都执行不到,此时整个程序都完全死去了。因为同步函数到死都不会交出 CPU 控制权。

就像这样:

var test = {
  status: false,
  main: function() {
    let res = this.func();
    console.log(res);
  },
  func: function() {
    while (!this.status);
    return 100;
  },
  confirm: function() {
    this.status = true;
  }
}

setTimeout(test.confirm.bind(test), 1000); // never confirm
test.func();

用 setTimeout 替代 while 轮询?

No

func: function() {
  if (!this.status) {
    setTimeout(() => {
      this.func();
    }, 500);
  } else {
    return 100;
  }
  // return undefined;
},

因为 async_func 依然是一个被同步调用的函数,所以在一开始 this.statusfalse 的时候,它执行了 setTimeout 后依然继续执行了 return 。不可能用 if 阻塞函数返回啊。不能因为你把 return 包裹起来这个函数就不返回了。只是没有显式执行,隐式执行了 return undefined

异步主函数 + Promise + while?

No

var test = {
  status: false,
  main: function() {
    let promise = this.async_func();
    promise.then( (data) => {
       console.log(data)
    });
  },
  async_func: function() {
    return new Promise((res) => {
		while(!this.status);
      res(100);
    });
  },
  confirm: function() {
    console.log('confirmed~');
    this.status = true;
  }
}
setTimeout(test.confirm.bind(test), 1000);
test.main(); // 100 immediately

这也印证了 Promise 并没有创建一个新的任务管理机制。并没有因为用 Promise 就自动在多个子线程间来回切换。

异步主函数 + Promise + setTimeout

Yes

async_func: function() {
  let helper = (res) => {
    if (!this.status) {
      setTimeout(() => {
        helper(res);
      }, 500);
      return;
    }
    res(100);
  }
  return new Promise((res) => {
    setTimeout(() => {
      helper(res);
    }, 500);
  })
},

可以简化为:

async_func: function() {
  let helper = (res) => {
    if (!this.status) {
      setTimeout(() => {
        helper(res);
      }, 1);
      return;
    }
    res(100);
  }
  return new Promise(helper);
},

Promise + Setter

Yes

var test = {
  status: false,
  on_status_change: () => {}, // placeholder
  setStatus: function(new_status) {
    this.status = new_status;
    this.on_status_change(new_status);
  },
  main: function() {
    let promise = this.async_func();
    promise.then((data) => {
       console.log(data)
    });
  },
  async_func: function() {
    let helper = (res) => {
      this.on_status_change = () => {
        res(100);
      }
    }
    return new Promise(helper);
  },
  confirm: function() {
    console.log('confirmed~');
    this.setStatus(true);
  }
}
setTimeout(test.confirm.bind(test), 1000);
test.main(); // 100 immediately