实现等待用户输入完毕继续执行

问题

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

用 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