Skip to content

Instantly share code, notes, and snippets.

@jcouyang
Last active February 19, 2020 09:58
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jcouyang/07ede61301bd27281859 to your computer and use it in GitHub Desktop.
Save jcouyang/07ede61301bd27281859 to your computer and use it in GitHub Desktop.

name: inverse layout: true class: center, middle, inverse

#并发,core.async 和 JavaScript .footnote[http://git.io/js-csp]


layout: false

What Will I Cover

  • 什么是并发

  • 什么是CSP

  • Clojure 是怎么做的

  • 为什么要这么做

  • JavaScript又该怎么做

???

这次session主要cover这些内容

所以如果你都没兴趣, 现在离开还来得及

这次session只有一个非常简单的例子, 我希望大家都能看懂, 哪里不懂的请及时打断我


Why

每样东西火起来都是有原因的

  • golang是难得的google自己在用的

  • 每一代人都会重新发现lisp语言的好处,我们这代也不例外 -- Clojure

  • JavaScript是一门很烂的语言, 但是是唯一浏览器支持的语言


Go Style Concurrency

当然Go不只是因为前面那个原因火的.

  • Go 使用非常特别的并发模型, 它实现了CSP并发编程, 并且起名叫 goroutines and channels,

  • 由于实在是太好用了, Clojure 也加入了CSP的阵营, 叫做 Core.async.

???

前段时间翻了一本叫函数式JavaScript的书, 书其实写的一般, 但有意思的是 作者同时也是 joy of clojure 的作者 Michael Fogus

他contribe过 underscore-contrib 以及 clojure

书中确实有一些东西来自clojure,但是不太多, 但是脑洞顿时被打开

似乎可以把其他语言好的东西搬到javascript, 要迁移就迁移个大的


并发(Concurrency)与并行(Parallelism)

如果我正在上班写代码,想加个班然后发个短信给老婆说晚点回, 发完以后继续敲代码. 那么发短信和敲代码两个任务就是 并发.

但如果我还特别喜欢音乐, 所以我边听音乐边敲代码, 那么交代吗和听音乐两个任务就是并行了.

???

解释并发与并行实在太简单不过了


并发与多线程

继续敲代码这个例子

  • 如果我现在fork出来一个手发短信
  • 但是我还是只有一个脑袋
  • 我的注意力只能集中在一个地方
  • 另外两只手只能放在键盘上什么也改不了, 直到短信发出去放下手机, 才能继续写代码.

??? 但是一般说到并发, 可能大家第一反应应该是多线程

比如 <-


template: inverse

但是如果我有几十个老婆

我就要fork这么多手


异步并发模型

跟多线程对应的是单线程(或一定数量的线程)的异步并发模型,比如JS的Event loop

  • 比如我还是两只手, 我正在写代码

  • 这时来了条短信

  • 我拿起手机看短信, 老婆叫早点回家

  • 不鸟, 放下手机继续敲代码.

注意这段动作与之前多线程的区别, 多线程的场景是我fork了第三只手, 而那只手在我敲代码是一直握着手机, 等着读短信.


.left-column[

JavaScript EventLoop

] .right-column[ ]

.left-column[

JavaScript EventLoop

] .right-column[

猜猜这段代码的输出是啥

JS Bin ]


总结下这两种并发模型

.left-column[

多线程的缺点

] .right-column[

  • low level
  • 阻塞
  • 开销大
  • 效率低
  • 不可预知
  • 难学
  • 难写
  • 难测
  • 难修

]

??? 这些痛学过多线程编程的人应该都懂吧

.left-column[

多线程的缺点

异步的缺点

] .right-column[

  • Callback Hell
  • high level
  • 难写
  • 难测
  • 难修
  • 难看 ]

??? 所以总的来说, 异步还是比多线程要好些, 毕竟level要高一些


template: inverse

不是有Promise吗

原来是一大坨 callback hell,现在是串起来的一大坨 callback hell

??? 于是CSP就来拯救你从callback hell中


CSP(Communicating Sequential Processes)

  • 通信顺序进程, 是计算机科学中用于一种描述并发系统中交互的形式语言, 简称CSP

  • 来源于C.A.R Hoare 1978年的论文.

  • 没错了, Hoare就是发明让我们算法课纠结得快挂科的快排算法那个家伙那位大牛.

??? 回转寿司


CSP Channel,Goroutines

CSP 的概念非常简单, 想象一下 event loop

  1. CSP 把这个event loop的消息队列转换成一个数据队列, 把这个队列叫做 channel
  2. 所有的任务等待对应队列中的数据

这样就成功的把任务成功从 callback hell 分离开来.

等等, 还是刚才发短信的例子, 我们来用CSP实现一遍


再看看我们的例子用callback是怎么玩得


template: inverse

来看看Clojure是怎么干的

.footnote[如果你不喜欢括号, go的csp例子可以看看12年的golang talk]

(def working (chan))
(def texting (chan))

(defn boss-yelling []
  (go-loop [no 1]
    (<! (timeout 1000))
    (>! working (str "bose say: work " no))
    (recur (+ no 1))))

(defn wife-texting []
  (go-loop []
    (<! (timeout 4000))
    (>! texting "wife say: come home!")
    (recur)))

(defn reading-text []
  (go-loop []
    (println (<! texting) "me: ignore")
    (recur)))

(defn work []
  (go-loop []
    (println (<! working) " me: working")
    (recur)))

(boss-yelling)
(wife-texting)
(work)
(reading-text)

不懂clojure没有关系,我可以解释,~~我不听我不听我不听!~~而且我还会在后面用JS实现一遍

???

这里面有四个任务, 他们并发执行, 而且任务之间不会callback来callback去

输出结果

JS Bin


与多线程和异步有什么不一样

  • 单线程
  • 同步的编程方式
  • 消除了callback
  • 并发任务分离
  • 好测
  • 好改

消除了Callback Hell

  • Before
askWife1(function(response1){
  if(response1)
    askWife2(function(response2){
      if(response2)
        askWife3(function(response3){
          if(response3)
            working();
        })
    })
})
  • After
(defn work []
  (go-loop []
    (askWifes)
    (when-let [response1 (<! wife1)
          response2 (<! wife2)
          response3 (<! wife3)]
      (println (<! working) " me: working"))
    (recur)))

??? 例子太简单完全看不出来优越感,我们来电变态的

原生 JS 也可以做到同样的事情

仅仅需要两个东西

  • es6 generator

  • 一个简单的状态机

???

Clojure的core.async这么好用, 能不能用在JS里啊

.left-column[

Generator

] .right-column[

var Gen = function*() {
  yield 1;
  yield 2;
  yield 3;
}
var gen = Gen()
gen.next()
// => Object { value: 1, done: false }
gen.next()
// => Object { value: 2, done: false }
gen.next()
// => Object { value: 3, done: false }
gen.next()
// => Object { value: undefined, done: true }

]

??? yield不是 ruby 的 yield, 是 python 的yield


状态机


看看我们需要实现些什么

  • goroutines

  • timeout

  • take (<!)

  • put (>!)


take <!

function take(chan) {
  return function() {
    if(chan.length === 0) {
      return ["park", null];
    } else {
      var val = chan.pop();
      return ["continue", val];
    }
  };
}

Goroutines

function go_(machine, step) {
  while(!step.done) {
    var arr   = step.value(),
        state = arr[0],
        value = arr[1];
    switch (state) {
      case "park":
        setTimeout(function() { go_(machine, step); },0);
        return;
      case "continue":
        step = machine.next(value);
        break;
    }
  }
}

function go(machine) {
  var gen = machine(); (ref:generator)
  go_(gen, gen.next());
}

???

  • 一个函数
  • 他可以接受一个 generator
  • 如果generator没有下一步,则结束
  • 如果该步的返回值状态为 park, 那么就是什么也不做, 过一会再来进入状态机尝试
  • 如果为 continue, 这接着generator下一步, 继续循环

CSP JS版 完整例子

JS Bin


the Future of JavaScript

Ecmascript 7 async function

async function textingWifes(){
  var response1 = await askWife1();
  var response2 = await askWife2();
  var response3 = await askWife3();
  if (response1 && response2 && response3)
    working();
}

See Also


name: last-page template: inverse

That's all folks!

Q & A ?

Slideshow hosted on gistdeck.

@wynn5a
Copy link

wynn5a commented Apr 5, 2016

你好,文章很棒,内容里面有类似这种 .left-column[ CSS 的东西,是因为使用了什么 md 不支持的东西么?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment