Javascript 延续传递样式与并发
我发现很多博客都提到并发/非阻塞/异步编程是延续传递风格(CPS)的一个优点。我不明白为什么CPS提供并发性,例如,人们提到Node.js是使用CPS实现的,尽管JavaScript是一种同步语言。有人会评论我的想法吗 首先,我对CPS的天真理解是,将某一点上的所有后续代码包装到一个函数中,并将该函数显式地作为参数传递。一些博客将continuation函数命名为Javascript 延续传递样式与并发,javascript,multithreading,haskell,asynchronous,concurrency,Javascript,Multithreading,Haskell,Asynchronous,Concurrency,我发现很多博客都提到并发/非阻塞/异步编程是延续传递风格(CPS)的一个优点。我不明白为什么CPS提供并发性,例如,人们提到Node.js是使用CPS实现的,尽管JavaScript是一种同步语言。有人会评论我的想法吗 首先,我对CPS的天真理解是,将某一点上的所有后续代码包装到一个函数中,并将该函数显式地作为参数传递。一些博客将continuation函数命名为return(),Gabriel Gonzalez称之为a,这两个都是精彩的解释 我的困惑主要来自一篇流行的博客文章。在本文的开头,A
return()
,Gabriel Gonzalez称之为a,这两个都是精彩的解释
我的困惑主要来自一篇流行的博客文章。在本文的开头,Axel Rauschmayer博士给出了两个代码片段,一个是同步程序,另一个是CPS中的异步程序(粘贴在此处以便于阅读)
同步代码:
function loadAvatarImage(id) {
var profile = loadProfile(id);
return loadImage(profile.avatarUrl);
}
function loadAvatarImage(id, callback) {
loadProfile(id, function (profile) {
loadImage(profile.avatarUrl, callback);
});
}
异步代码:
function loadAvatarImage(id) {
var profile = loadProfile(id);
return loadImage(profile.avatarUrl);
}
function loadAvatarImage(id, callback) {
loadProfile(id, function (profile) {
loadImage(profile.avatarUrl, callback);
});
}
我不明白为什么CPS是异步的。在我读了另一篇文章之后,我认为代码可能有一个假设:函数loadProfile()
和loadImage()
本身就是异步函数。那么,使其异步的不是CPS。在中,作者实际展示了fetch()
的一个实现,它类似于前面博客中的loadProfile()
。fetch()
函数通过调用req.onreadystatechange
来明确假设底层并发执行模型。这让我想到,可能不是CPS提供了并发性
假设底层函数是异步的,那么我将进入第二个问题:我们可以在没有CPS的情况下编写异步代码吗?考虑函数loadProfile()
的实现。如果不是因为CPS,它是异步的,为什么我们不能采用相同的机制异步实现loadAvatarImage()
?假设loadProfile()
使用fork()
创建一个新线程,在主线程以非阻塞方式执行时发送请求并等待响应,我们可以对loadAvatarImage()
执行同样的操作
我给它一个回调函数updateDOM()
。如果没有updateDOM()
,将其与CPS版本进行比较是不公平的——CPS版本有关于获取图像后要做什么的额外信息,即callback
函数,但原始的同步loadAvatarImage()
没有
有趣的是,@DarthFennec指出我新的loadAvatarImage()
实际上是CPS:fork()
是CPS,act()
是CPS(如果我们明确地给它updateDOM
),而loadAvatarImage()
是CPS。该链使loadAvatarImage()
异步loadProfile()
和loadImage()
不需要是异步或CPS
如果这里的推理是正确的,我能得到这两个结论吗
loadProfile()
、loadImage()
、fetch()
、或fork()
,则只能以CPS样式编码以确保异步API被异步使用,例如返回loadImage(profile.avatarUrl)
将使loadImage()的并发性无效。
- Javascript是非并行的,因为它在单个线程中运行;它通过交错多个执行线程来实现并发,而不是实际同时运行它们
- Javascript是协作的,因为调度器只在当前线程请求时切换到不同的线程。另一种选择是抢占式调度,调度程序决定在任何时候随意切换线程
'click'
事件中;单击按钮时,回调将运行。事实证明,同样的方法也可以用于其他目的。假设你想等一分钟,然后做一件事。天真的方法是将Javascript线程暂停60秒,这(如上所述)将导致页面在此期间崩溃。但是,如果计时器作为UI事件公开,则调度程序可以挂起该线程,同时允许其他线程运行。然后,计时器将以与按钮pre相同的方式执行回调
run(function() {
fork(function() {
console.print("thread 1, first line")
fork(function() {
console.print("thread 1, second line")
})
})
fork(function() {
console.print("thread 2, first line")
fork(function() {
console.print("thread 2, second line")
})
})
})
thread 1, first line
thread 2, first line
thread 1, second line
thread 2, second line