摘要:發(fā)現(xiàn)一個(gè)月沒刷技術(shù)文章了有點(diǎn)慌整理一篇短的用法出來只包含最基本的用法在里邊最清晰不過我是在寫的版本的實(shí)現(xiàn)包含異步用法會(huì)更繁瑣一些但是也值得看看我相信普及之前還是一個(gè)很有意思的選擇我的代碼寫的是可以自動(dòng)腦補(bǔ)圓括號(hào)花括號(hào)上去注意包含的函數(shù)自動(dòng)
發(fā)現(xiàn)一個(gè)月沒刷技術(shù)文章了, 有點(diǎn)慌, 整理一篇短的 CSP 用法出來,
只包含最基本的用法, 在 Go 里邊最清晰, 不過我是在 Clojure 寫的 CSP.
js 版本的 CSP 實(shí)現(xiàn)包含異步, 用法會(huì)更繁瑣一些, 但是也值得看看.
我相信 async/await 普及之前, js-csp 還是一個(gè)很有意思的選擇.
我的代碼寫的是 CoffeeScript, 可以自動(dòng)腦補(bǔ)圓括號(hào)花括號(hào)上去...
注意包含 yield 的函數(shù)自動(dòng)被轉(zhuǎn)成 function*() {}, 所以注意腦補(bǔ).
腦補(bǔ)不出來的只好貼在這邊編譯下了 http://coffeescript.org/
首先是最基本的 CSP 的例子, 也就是用同步的代碼寫異步的邏輯,
CSP 當(dāng)中最核心的概念是 Channel, 最簡單的 csp.timeout(1000) 創(chuàng)建 channel.
csp = require "js-csp" # 用 csp.go 啟動(dòng)一個(gè) yield 函數(shù) csp.go -> # 有 csp.take 從這個(gè)管道取出數(shù)據(jù), yield 來模擬阻塞的效果 yield csp.take csp.timeout(1000) console.log "Gone 1s"
運(yùn)行一下:
=>> coffee async.coffee Gone 1s
我注意到對于 timeout 來說, 省掉 csp.take 也是能夠正常運(yùn)行的:
csp = require "js-csp" csp.go -> # 腦補(bǔ) yield 函數(shù) yield csp.timeout 1000 console.log "Gone 1s" yield csp.timeout 2000 console.log "Gone 2s" yield csp.timeout 3000 console.log "Gone 3s. End"
運(yùn)行一下:
=>> coffee async.coffee Gone 1s Gone 2s Gone 3s. End使用 put 和 take
csp.timeout 比較特殊, 默認(rèn)就會(huì)產(chǎn)生數(shù)據(jù), 只要進(jìn)行 csp.take 就好了.
一般的 Channel 的話, 需要手動(dòng)創(chuàng)建出來, 然后手動(dòng)推數(shù)據(jù),
比如下面的代碼創(chuàng)建了一個(gè)數(shù)據(jù), 用 csp.go 啟動(dòng)另一個(gè)"進(jìn)程"往 Channel 推數(shù)據(jù),
這里的"進(jìn)程"的說法并不是真正的進(jìn)程, 只是模擬進(jìn)程的行為:
csp = require "js-csp" talk = (ch) -> yield csp.timeout 3000 console.log "Done 3s timeout" # 等待 3s 然后往 Channel 當(dāng)中寫入數(shù)據(jù), yield 會(huì)產(chǎn)生等待 yield csp.put ch, "some result" csp.go -> ch = csp.chan() # 啟動(dòng)另一個(gè)"進(jìn)程" csp.go talk, [ch] # 數(shù)組里是傳給 talk 函數(shù)的參數(shù) # 使用 yield.take 從 Channel 取出數(shù)據(jù), 使用 yield 模擬等待 result = yield csp.take ch console.log "Result:", JSON.stringify(result)
運(yùn)行一下:
=>> coffee async.coffee Done 3s timeout Result: "some result"假裝有兩個(gè)進(jìn)程
同樣是上邊的代碼, 只是調(diào)整一下寫法, 看上去像是分別啟動(dòng)了兩個(gè)"進(jìn)程",
雖然它們的運(yùn)行時(shí)獨(dú)立的, 但是可以通過管道進(jìn)行通信,
而且在對應(yīng)的 csp.take 和 csp.put 操作過程中, 會(huì)通過 yield 進(jìn)行等待:
csp = require "js-csp" talk = (ch) -> yield csp.timeout 3000 console.log "Done 3s timeout" yield csp.put ch, "some result" listen = (ch) -> result = yield csp.take ch console.log "Result:", JSON.stringify(result) # 創(chuàng)建 Channel, 啟動(dòng)兩個(gè)"進(jìn)程" theCh = csp.chan() # csp.go 后面第一個(gè)是 yield 函數(shù), 第二個(gè)是參數(shù)的數(shù)組, 雖然比較難看 csp.go talk, [theCh] csp.go listen, [theCh]
運(yùn)行一下:
=>> coffee async.coffee Done 3s timeout Result: "some result"封裝異步事件
實(shí)際使用當(dāng)中, 會(huì)需要把 js 環(huán)境的異步代碼封裝成管道的形式,
不封裝成管道, 就不能借助 csp.go 來封裝同步代碼了,
由于 js 不像 Go 那樣整個(gè)語言層面做了處理, 實(shí)際上會(huì)有奇怪的寫法,
所以 js-csp 提供了 csp.putAsync 和 csp.takeAsync:
csp = require "js-csp" talk = (ch) -> setTimeout -> csp.putAsync ch, "some result" console.log "Finished 3s of async" , 3000 listen = (ch) -> result = yield csp.take ch console.log "Result:", JSON.stringify(result) theCh = csp.chan() talk theCh csp.go listen, [theCh]
運(yùn)行一下:
=>> coffee async.coffee Finished 3s of async Result: "some result"處理超時(shí)
一個(gè)操作是否超時(shí)的問題, 可以同時(shí)啟動(dòng)一個(gè)定時(shí)的"進(jìn)程",
然后觀察兩個(gè)"進(jìn)程"哪一個(gè)先執(zhí)行完成, 從而判斷是否超時(shí),
這就用到了 csp.alts 函數(shù), 這個(gè)奇怪的命名是用 Clojure 帶過來的:
csp = require "js-csp" talk = (ch) -> time = Math.random() * 4 * 1000 setTimeout -> console.log "Get result after #{time}ms" csp.putAsync ch, "some result" , time listen = (ch) -> hurry = csp.timeout 2000 # 通過 csp.alts 同時(shí)等待多個(gè) Channel 返回?cái)?shù)據(jù) result = yield csp.alts [ch, hurry] # result.channel 可以用于判斷數(shù)據(jù)的來源, result.value 才是真正的數(shù)據(jù) if result.channel is hurry console.log "Too slow, got no result" # close 只是設(shè)置 Channel 的狀態(tài), 其實(shí)還需要手工處理一些邏輯 hurry.close() else console.log "Fast enough, got", JSON.stringify(result.value) theCh = csp.chan() talk theCh csp.go listen, [theCh]
用了隨機(jī)數(shù), 運(yùn)行多次試一下, 可以看到根據(jù)不同的時(shí)間, 結(jié)果是不一樣的:
=>> coffee async.coffee Too slow, got no result Get result after 3503.6168682995008ms =>> coffee async.coffee Too slow, got no result Get result after 3095.264637685924ms =>> coffee async.coffee Get result after 703.6501633183257ms Fast enough, got "some result" =>> coffee async.coffee Too slow, got no result Get result after 3729.5125755664317ms =>> coffee async.coffee Get result after 101.51519531067788ms Fast enough, got "some result"循環(huán)任務(wù)
跟 yield 用法類似, 如果有循環(huán)的代碼, 也可以用 CSP 寫出來,
這個(gè)的話不用怎么想應(yīng)該能明白了, loop 只是 while true 的語法糖:
csp = require "js-csp" chatter = (ch) -> counter = 0 loop yield csp.timeout 1000 counter += 1 yield csp.put ch, counter repeat = (ch) -> loop something = yield csp.take ch console.log "Hear something:", something theCh = csp.chan() csp.go chatter, [theCh] csp.go repeat, [theCh]
運(yùn)行一下:
=>> coffee async.coffee Hear something: 1 Hear something: 2 Hear something: 3 Hear something: 4 ^C多個(gè)數(shù)據(jù)的消費(fèi)者
實(shí)際場景當(dāng)中會(huì)遇到多個(gè)消費(fèi)者從單個(gè)生產(chǎn)者讀取數(shù)據(jù)的需求,
這是一個(gè)用 Channel 比較合適的場景, 啟動(dòng)兩個(gè)"進(jìn)程"讀取一個(gè) Channel 就好了,
下面我模擬的是不同的處理時(shí)間 300ms 和 800ms 讀取 100ms 頻率的數(shù)據(jù),
因?yàn)?CSP 自動(dòng)處理了等待, 整個(gè)代碼看上去挺簡單的:
csp = require "js-csp" chatter = (ch) -> counter = 0 loop yield csp.timeout 100 counter += 1 yield csp.put ch, counter repeat = (ch) -> loop yield csp.timeout 800 something = yield csp.take ch console.log "Hear at 1:", something repeat2 = (ch) -> loop yield csp.timeout 300 something = yield csp.take ch console.log "Hear at 2:", something theCh = csp.chan() csp.go chatter, [theCh] csp.go repeat, [theCh] csp.go repeat2, [theCh]
運(yùn)行一下:
=>> coffee async.coffee Hear at 2: 1 Hear at 2: 2 Hear at 1: 3 Hear at 2: 4 Hear at 2: 5 Hear at 2: 6 Hear at 1: 7 Hear at 2: 8 Hear at 2: 9 Hear at 1: 10 Hear at 2: 11 Hear at 2: 12 Hear at 2: 13 Hear at 1: 14 Hear at 2: 15 Hear at 2: 16 Hear at 1: 17 Hear at 2: 18 Hear at 2: 19 Hear at 2: 20 Hear at 1: 21 Hear at 2: 22 Hear at 2: 23 Hear at 1: 24 ^C使用 buffer
默認(rèn)情況下管道是阻塞的, csp.put csp.take 成對進(jìn)行,
也就是說, 只有一個(gè)就緒的話, 它會(huì)等待另一個(gè)開始, 然后一起執(zhí)行,
但是用 buffer 的話, 管道就會(huì)先在一定范圍內(nèi)進(jìn)行緩存,
這樣 csp.put 就可以先運(yùn)行下去了, 這個(gè)是不難理解的...
管道實(shí)際上有 3 種策略, fixed, dropping, sliding:
fixed, 緩存放滿以后就會(huì)開始形成阻塞了
dropping, 緩存滿了以后, 新的數(shù)據(jù)就會(huì)丟棄
sliding, 緩存滿以后, 會(huì)丟棄掉舊的數(shù)據(jù)讓新數(shù)據(jù)能放進(jìn)緩存
隨便演示一個(gè)丟棄數(shù)據(jù)的例子:
csp = require "js-csp" chatter = (ch) -> counter = 0 loop yield csp.timeout 200 counter += 1 console.log "Write data:", counter yield csp.put ch, counter repeat = (ch) -> loop yield csp.timeout 300 something = yield csp.take ch console.log "Hear:", something theCh = csp.chan(csp.buffers.dropping(3)) csp.go chatter, [theCh] csp.go repeat, [theCh]
運(yùn)行一下, 可以看到 "Hear" 部分丟失了一些數(shù)據(jù), 但前三個(gè)數(shù)據(jù)不會(huì)丟:
=>> coffee async.coffee Write data: 1 Hear: 1 Write data: 2 Hear: 2 Write data: 3 Write data: 4 Hear: 3 Write data: 5 Hear: 4 Write data: 6 Write data: 7 Hear: 5 Write data: 8 Hear: 6 Write data: 9 Write data: 10 Hear: 7 Write data: 11 Hear: 8 Write data: 12 Write data: 13 Hear: 9 Write data: 14 Hear: 11 Write data: 15 Write data: 16 Hear: 12 Write data: 17 Hear: 14 ^C小結(jié)
由于 CSP 是在 Go 語言發(fā)明的, 完整的用法還是看 Go 的教程比較好,
到了 Clojure 和 js 當(dāng)中難免會(huì)增加一些坑, 特別是 js 當(dāng)中...
上面提到的 API 在 js-csp 的文檔上有描述, 例子也有, 但是挺少的:
https://github.com/ubolonton/...
https://github.com/ubolonton/...
另外還有一些高級一點(diǎn)的用法, 比如數(shù)據(jù)的 transform 和 pipe 之類的,
其實(shí)就是 Stream 的用法在 Channel 上的改版, 某種程度上 Channel 也是 Stream,
對于我個(gè)人來說, Channel 的抽象比起 Stream 的抽象舒服多了.
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/86652.html
摘要:的用例的用法最早是語言傳開來的看一下我從網(wǎng)上扒的代碼其中符號(hào)是往當(dāng)中寫入數(shù)據(jù)的操作同時(shí)注意一般的位置對于來說是阻塞的由于能夠處理異步操作也就是說能做到異步代碼用同步寫法更多的細(xì)節(jié)搜索應(yīng)該就能找到除了也實(shí)現(xiàn)了對于的支持也就是 CSP 的用例 CSP 的用法最早是 Go 語言傳開來的, 看一下我從網(wǎng)上扒的代碼: package main import fmt func ping(pin...
摘要:原文的個(gè)示例是什么一般來說它是寫并行代碼的一套方案在語言里自帶該功能通過基于的來實(shí)現(xiàn)現(xiàn)在通過也能做支持了或者說的功能為什么我要關(guān)心因?yàn)樗鼜?qiáng)大啊而且高效而且簡單都這樣了你還想要什么好吧說細(xì)節(jié)怎樣使用呢我們用而且需要支持才有也就說或者更高的版 原文 http://lucasmreis.github.io/b... Communicating Sequential Processes 的 7...
摘要:前言今天群里面有很多都在問關(guān)于組件之間是如何通信的問題,之前自己寫的時(shí)候也遇到過這類問題。英文能力有限,如果有不對的地方請跟我留言,一定修改原著序處理組件之間的交流方式,主要取決于組件之間的關(guān)系,然而這些關(guān)系的約定人就是你。 前言 今天群里面有很多都在問關(guān)于 React 組件之間是如何通信的問題,之前自己寫的時(shí)候也遇到過這類問題。下面是我看到的一篇不錯(cuò)英文版的翻譯,看過我博客的人都知道...
摘要:關(guān)鍵字表示代碼在該處將會(huì)被阻塞式暫停阻塞的僅僅是函數(shù)代碼本身,而不是整個(gè)程序,但是這并沒有引起函數(shù)內(nèi)部自頂向下代碼的絲毫改變。通過實(shí)現(xiàn)模式在通過實(shí)現(xiàn)理論的過程中已經(jīng)有一些有趣的探索了。 至此本系列的四篇文章翻譯完結(jié),查看完整系列請移步blogs 由于個(gè)人能力知識(shí)有限,翻譯過程中難免有紕漏和錯(cuò)誤,望不吝指正issue ES6 Generators: 完整系列 The Basics...
摘要:總結(jié)這篇文章簡單的介紹了一些常用的異步編程的方法,如果有錯(cuò)誤或不嚴(yán)謹(jǐn)?shù)牡胤剑瑲g迎批評指正,如果喜歡,歡迎點(diǎn)贊收藏。 大家都知道js的執(zhí)行環(huán)境是單線程的,如果沒有異步編程,那么js的執(zhí)行效率會(huì)非常低下,導(dǎo)致程序十分卡頓,一提到異步編程大家首先的想到的一定是回調(diào)函數(shù),這也是最常用的異步編程的形式,但其實(shí)常用的還有Promise和Async函數(shù),接下來就讓我們一起學(xué)習(xí)這幾種常用的異步編程方法...
閱讀 2946·2021-11-22 15:25
閱讀 2240·2021-11-18 10:07
閱讀 1045·2019-08-29 15:29
閱讀 472·2019-08-29 13:25
閱讀 1504·2019-08-29 12:58
閱讀 3201·2019-08-29 12:55
閱讀 2911·2019-08-29 12:28
閱讀 500·2019-08-29 12:16