资讯专栏INFORMATION COLUMN

[译] 快速介绍 JavaScript 中的 CSP

Rocko / 1177人阅读

摘要:原文的个示例是什么一般来说它是写并行代码的一套方案在语言里自带该功能通过基于的来实现现在通过也能做支持了或者说的功能为什么我要关心因为它强大啊而且高效而且简单都这样了你还想要什么好吧说细节怎样使用呢我们用而且需要支持才有也就说或者更高的版

原文 http://lucasmreis.github.io/b...

Communicating Sequential Processes 的 7 个示例

CSP 是什么? 一般来说, 它是写并行代码的一套方案.
在 Go 语言里自带该功能, Clojure 通过基于 Macro 的 core.async 来实现,
现在 JavaScript 通过 Generator 也能做支持了, 或者说 ES6 的功能.

为什么我要关心 CSP? 因为它强大啊, 而且高效, 而且简单. 都这样了你还想要什么? :)

好吧, 说细节. 怎样使用呢?我们用 js-csp, 而且需要 generator 支持, ES6 才有.
也就说 Node 4 或者更高的版本才行, 或者浏览器代码用 Babel 编译一下,
当然能其他的编译工具可能也行, 但你要确认下是支持 Generator 的.

注: 文章写得早, 现在翻译文章, Chrome 应该是支持 Generator 的.

扯多了, 来看例子吧!

例 1: 进程

第一个要学的概念是"进程". 进程可以执行代码, 简单说就是这样的了. :)

注: 当然不是操作系统原始的进程了, js 里模拟的.

这是启动进程的语法: generator 函数作为参数, 传给 go 函数执行.

import {go} from "js-csp";

go(function* () {
  console.log("something!");
});

// terminal output:
//
// => something!
例 2: 进程可以暂停

使用 yield 关键字可以暂停一个进程, 把当前进程的占用释放:

import {go, timeout} from "js-csp";

go(function* () {
  yield timeout(1000);
  console.log("something else after 1 second!");
});

console.log("something!");

// terminal output:
//
// => something!
// => something else after 1 second!
例 3: 进程等待来自管道的数据

第二个要学的概念是管道, 也是最后一个了. 管道就像是队列.
一旦进程对管道调用 take, 进程就会暂停, 直到别人往管道放进数据.

import {go, chan, take, putAsync} from "js-csp";

let ch = chan();

go(function* () {
  const received = yield take(ch);
  console.log("RECEIVED:", received);
});

const text = "something";
console.log("SENDING:", text);

// use putAsync to put a value in a
// channel from outside a process
putAsync(ch, text);

// terminal output:
//
// => SENDING: something
// => RECEIVED: something
例 4: 进程通过管道来通信

管道的另一边, 往管道里 put 数据的那些进程也会暂停, 直到这边进程调用 take.

下面的例子就复杂一点了, 试着跟随一下主线, 印证一下终端输出的内容:

import {go, chan, take, put} from "js-csp";

let chA = chan();
let chB = chan();

// Process A
go(function* () {
  const receivedFirst = yield take(chA);
  console.log("A > RECEIVED:", receivedFirst);

  const sending = "cat";
  console.log("A > SENDING:", sending);
  yield put(chB, sending);

  const receivedSecond = yield take(chA);
  console.log("A > RECEIVED:", receivedSecond);
});

// Process B
go(function* () {
  const sendingFirst = "dog";
  console.log("B > SENDING:", sendingFirst);
  yield put(chA, sendingFirst);

  const received = yield take(chB);
  console.log("B > RECEIVED:", received);

  const sendingSecond = "another dog";
  console.log("B > SENDING:", sendingSecond);
  yield put(chA, sendingSecond);
});

// terminal output:
//
// => B > SENDING: dog
// => A > RECEIVED: dog
// => A > SENDING: cat
// => B > RECEIVED: cat
// => B > SENDING: another dog
// => A > RECEIVED: another dog
立 5: 管道也是队列

由于管道是队列, 当进程从管道取走数据, 其他进程就拿不到了.
所以推数据的是一个进程, 取数据的也是一个进程.

下面这个例子可以看到第二个进程永远不会打印 B > RECEIVED: dog,
因为第一个进程已经把数据取走了.

import {go, chan, take, put} from "js-csp";

let ch = chan();

go(function* () {
  const text = yield take(ch);
  console.log("A > RECEIVED:", text);
});

go(function* () {
  const text = yield take(ch);
  console.log("B > RECEIVED:", text);
});

go(function* () {
  const text = "dog"
  console.log("C > SENDING:", text);
  yield put(ch, text);
});

// terminal output:
//
// => C > SENDING: dog
// => A > RECEIVED: dog
例 6: 带缓冲的管道不会在 put 操作时阻塞

管道可以带缓冲, 也就是, 一定数量之内的数据, 执行 put 操作可以避开阻塞.

这个例子里, 即便没有其他进程调用 take, 前两个写操作也不会阻塞进程.
不过管道的缓存数量是 2, 所以第三个数据就阻塞进程了, 直到其他进程取走数据.

import {go, chan, put, buffers} from "js-csp";

let ch = chan(buffers.fixed(2));

go(function* () {
  yield put(ch, "value A");
  yield put(ch, "value B");
  console.log("I should print!");
  yield put(ch, "value C");
  console.log("I should not print!");
});

// terminal output:
//
// => I should print!
例 7: Dropping And Sliding Buffers

固定大小的缓冲在 N 个数据之后会阻塞, 初次之外, 还有对缓冲的 dropping 和 sliding 控制.

缓冲的 dropping 以为着管道可以持有 N 个数据.
再增加额外的数据放进管道, 管道就会将其丢弃.

缓冲的 sliding 也可以持有 N 个数据. 不过相对于直接丢弃新数据,
sliding 缓冲原先的第一个推的数据会被丢弃, buffer 里会留下新的这个数据.

下面这个例子, value Bvalue C 在 dropping 缓冲里被丢弃, 因为已经有 value A 了.
第二个进程里, 当 value B 被放进管道, value A 就被丢弃了.
然后 value C 放进管道, value B 就被丢弃.

根据它们的工作原理, dropping 和 sliding 的缓冲永远不会阻塞!

let droppingCh = chan(buffers.dropping(1));
let slidingCh  = chan(buffers.sliding(1));

go(function* () {
  yield put(droppingCh, "value A");
  yield put(droppingCh, "value B");
  yield put(droppingCh, "value C");
  console.log("DROPPING:", yield take(droppingCh));
});

go(function* () {
  yield put(slidingCh, "value A");
  yield put(slidingCh, "value B");
  yield put(slidingCh, "value C");
  console.log("SLIDING:", yield take(slidingCh));
});

// terminal output:
//
// => DROPPING: value A
// => SLIDING: value C
结论

CSP 用了一段时间之后, 用回调或者 Promise 写代码就像是侏罗纪的技术.
我希望 ES6 的 Generator 能帮助 CSP 成为 JavaScript 的一个标准,
就像是 Go 已经是的那样, 以及 Clojure 里正在成为的那样.

下一步

另外有两个模型也还有意思, 大概可以认为是比 CSP 层级更高一点的:
函数式也是响应式编程(Rx)跟 Actors, 分别在 Rx 和 Erlang 里用到.
我当然后面也会写博客来挖掘一下.

我同时相信 CSP 对于前端框架来说非常棒.

原作者还有一个文章可以看下: Using CSP as Application Architecture

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90952.html

相关文章

  • 前端必须知道的 HTTP 安全头配置

    摘要:在本文中,我将介绍常用的安全头信息设置,并给出一个示例。响应头指定了该响应的资源是否被允许与给定的共享。示例指定可以送达的域名,默认为当前域名不包含子域名只有在协议时才会被发送到服务端。 在本文中,我将介绍常用的安全头信息设置,并给出一个示例。在本文的最后,我将介绍用于常见应用程序和web服务器的安全头信息示例设置。 Content-Security-Policy 内容安全策略(CSP...

    OnlyMyRailgun 评论0 收藏0
  • Web 应用安全性: 使用这些 HTTP 头保护 Web 应用

    摘要:综上所述,认为没有提供的保护,用户会过得更好安全研究人员并不完全反对这一决定。内容安全策略是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本和数据注入攻击等。 这是关于web安全性系列文章的第 三 篇,其它的可点击以下查看: Web 应用安全性: 浏览器是如何工作的 Web 应用安全性: HTTP简介 目前,浏览器已经实现了大量与安全相关的头文件,使攻击者更难利用漏...

    spademan 评论0 收藏0
  • Alibaba Sentinel(1):快速上手

    摘要:是一个灵活的系统负载控制框架,通过控制接口和方法的调用来保证系统负载不会过大,维持正常响应速度。创建一个项目首先创建一个空的项目,加上的依赖。编写控制规则将控制规则包装为类。超过这个阈值则会拒绝调用该方法。 Alibaba Sentinel 是一个灵活的系统负载控制框架,通过控制接口和方法的调用来保证系统负载不会过大,维持正常响应速度。 该项目的地址是 https://github.c...

    hover_lew 评论0 收藏0

发表评论

0条评论

Rocko

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<