我是JavaScript的新手,我习惯于通过创建工作线程来在Java中创建异步无阻塞代码。
鉴于 JavaScript是单线程语言,我不理解异步无阻塞代码在JavaScript中如何工作。
例如,JavaScript中的承诺和回调。
两者都不阻塞,并且允许主线程逐行继续执行程序的其余部分,并且仅当在稍后的时间(例如数据准备就绪)实现了promise时,promise.resolve()或执行回调。
现在,我很难理解哪个线程准确地跟踪了何时实现/准备好诺言,或者如果主线程已经继续执行并忙于执行其他操作,则随时可以执行回调?
我的逻辑告诉我,作为Java程序员,必须有一个后台工作线程负责在准备好执行回调/承诺时通知主线程,这与JavaScript是单线程这一事实相矛盾,因此我一定是错的。
我希望对此概念做一个很好的解释。 预先感谢!
答案 0 :(得分:3)
有两种方法可以使用计算机实现并发行为:
您同时运行两台计算机(或处理器)(多线程)。
您可以在不同的任务之间快速切换,从而看起来好像它们可以同时运行(多任务)。
现在,尽管Java经常在这些任务之间切换,但是JavaScript仅在当前任务完成时才切换任务;因此,该代码不会同时运行,并且表现为“单线程”。
以“单线程方式”执行JavaScript并不意味着底层引擎是单线程的。事实并非如此,它使用一些线程来管理I / O。每当您启动异步操作时,引擎就会将其委派给一个后台线程。然后,当任务完成时,另一个线程将结果返回到主线程,然后该主线程将调回JavaScript代码。
您可以将JavaScript引擎视为TaskExecutor
中启动线程的ThreadPool
。但是,您只能控制TaskExecutor
。
答案 1 :(得分:1)
承诺 / 回调是阻止。您可以运行以下示例:
let shiftIsPressed = false;
function setEventsCopyable(isCopyable) {
shiftIsPressed = !shiftIsPressed;
calendar.setOption("droppable", isCopyable);
calendar.setOption("editable", !isCopyable);
}
document.addEventListener("keydown", event => {
if (event.keyCode === 16 && !shiftIsPressed) {
setEventsCopyable(true);
}
});
document.addEventListener("keyup", event => {
if (shiftIsPressed) {
setEventsCopyable(false);
}
});
let containerEl = document.getElementById("calendar");
let calendarEl = document.getElementById("calendar");
new Draggable(containerEl, {
itemSelector: ".fc-event",
eventData: function(eventEl) {
return {
title: eventEl.innerText
};
}
});
var calendar = new Calendar(calendarEl, {
plugins: ["dayGrid", "interaction"],
defaultView: "dayGridMonth",
// Determines whether the events on the calendar can be modified.
editable: true,
// Determines if external draggable elements can be dropped onto the calendar.
dropAccept(el) {
return shiftIsPressed;
},
events: [
{
title: "Test 1",
start: "2019-04-01"
},
{
title: "Test 2",
start: "2019-04-03",
end: "2019-04-05"
},
{
title: "Test 3",
start: "2019-04-22",
end: "2019-04-25"
},
{
title: "Test 4",
start: "2019-04-19"
}
]
});
calendar.render();
您将看到function func1() {
return new Promise(resolve => {
setTimeout(() => {
console.log('func1');
while (true) {}
resolve();
}, 0);
});
}
function func2() {
func1();
func1();
console.log('func2');
}
func2();
仅被打印一次,因为执行的第一个func1()的Promise阻止了第二个函数的调用。
您可能会考虑将每个代码块都放在(事件循环的)队列中。
对于异步调用,代码的异步部分被排队,等待事件循环运行。当然,事件循环本身就是JavaScript应用程序中的单线程。
例如,如果将while循环移动到func1
的末尾,您将永远不会看到func2()
被打印出来,因为函数func2()阻塞了队列,因此Promise将永不解决。
用作调度程序的“线程”应该是应用程序之外的另一个线程,但是由于它不是应用程序的一部分(由JavaScript开发人员/用户编写),因此我认为JavaScript并非“单线程”语言”。