是否继续传递风格与管道有什么不同?

Phi*_*l H 17 javascript pipe continuation-passing

我一直在学习延续传递样式,特别是在javascript中实现的异步版本,其中一个函数将另一个函数作为最终参数并创建一个异步调用,将返回值传递给第二个函数.

但是,我不能完全看到延续传递除了重新创建管道(如在unix命令行管道中)或流之外还有什么作用:

replace('somestring','somepattern', filter(str, console.log));
Run Code Online (Sandbox Code Playgroud)

VS

echo 'somestring' | replace 'somepattern' | filter | console.log
Run Code Online (Sandbox Code Playgroud)

除了管道更多,更清洁.使用管道,很明显数据被传递,同时执行被传递给接收程序.事实上,对于管道,我希望数据流能够继续向下传递管道,而在CPS中我期望一个串行过程.

或许可以想象,如果通信对象和更新方法与数据一起传递,CPS可以扩展到连续管道,而不是完整的切换和返回.

我错过了什么吗?CPS在某些重要方面有所不同(更好吗?)?

要清楚,我的意思是继续传递,其中一个函数将执行传递给另一个函数,而不仅仅是普通的回调.CPS似乎暗示将函数的返回值传递给另一个函数,然后退出.

Mar*_*oth 9

UNIX管道与异步javascript

unix管道的行为方式与链接的异步CPS代码之间存在很大的根本区别.

主要是管道阻止执行直到整个链完成,而异步CPS示例将在第一次异步调用后立即返回,并且只在完成后才执行回调.(完成超时等待时,在您的示例中.)

看看这个例子.我将使用Fetch API和Promises来演示异步行为而不是setTimeout,以使其更加真实.想象一下,第一个函数f1()负责调用一些web服务并将结果解析为json.这是"管道"进入f2()处理结果.

CPS风格:

function f2(json){
    //do some parsing
}

function f1(param, next) {
   return fetch(param).then(response => response.json()).then(json => next(json));
}

// you call it like this:
f1("https://service.url", f2);
Run Code Online (Sandbox Code Playgroud)

如果你将调用f1移出f1,你可以编写一些在语法上看起来像管道的东西,但这与上面完全相同:

function f1(param) {
   return fetch(param).then(response => response.json());
}

// you call it like this:
f1("https://service.url").then(f2);
Run Code Online (Sandbox Code Playgroud)

但这仍然不会阻止.你不能在javascript中使用阻塞机制来完成这个任务,根本没有阻止Promise的机制.(在这种情况下,您可以使用同步XMLHttpRequest,但这不是重点.)

CPS与管道

上述两种方法之间的区别在于谁拥有控制权来决定是否调用下一步以及确切地调用哪些参数,调用者(后面的例子)或被调用的函数(CPS).

CPS非常方便的一个很好的例子是中间件.例如,在处理管道中考虑缓存中间件.简化示例:

function cachingMiddleware(request, next){
     if(someCache.containsKey(request.url)){
         return someCache[request.url];
     }
     return next(request);
}
Run Code Online (Sandbox Code Playgroud)

中间件执行一些逻辑,检查缓存是否仍然有效:

  • 如果不是,则next调用,然后继续处理管道.

  • 如果它有效,则返回缓存的值,跳过下一次执行.