葡京娱乐总站平台ES6 Generators并发

1. 如何创建Looper?

Looper的构造方法为private,所以不能够直接利用其构造方法创建。

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

倘惦记以现阶段线程创建Looper,需用Looper的prepare方法,Looper.prepare()。

如果现在使我们来实现Looper.prepare()这个方法,我们欠怎么开?我们清楚,Android中一个线程最多只能发出一个Looper,若在曾来Looper的线程中调用Looper.prepare()会抛出RuntimeException(“Only
one Looper may be created per
thread”)。面对如此的求,我们可能会见考虑用一个HashMap,其中Key为线程ID,Value为与线程关联的Looper,再长有并机制,实现Looper.prepare()这个方法,代码如下:

public class Looper {

    static final HashMap<Long, Looper> looperRegistry = new HashMap<Long, Looper>();

    private static void prepare() {
        synchronized(Looper.class) {
            long currentThreadId = Thread.currentThread().getId();
            Looper l = looperRegistry.get(currentThreadId);
            if (l != null)
                throw new RuntimeException("Only one Looper may be created per thread");
            looperRegistry.put(currentThreadId, new Looper(true));
        }
    }
    ...
}

  ES6 Generators系列:

  1. ES6
    Generators基本概念
  2. 深刻钻研ES6 Generators
  3. ES6
    Generators的异步应用
  4. ES6 Generators并发

  如果您已经读了之系列的前头三篇稿子,那么您早晚对ES6
generators非常了解了。希望您可知从中有所得并被generator发挥它们实在的用意。最后咱们设探讨的是主题可能会见让您血脉喷张,让您绞尽脑汁(说实话,写就首文章为自己死费脑子)。花点时间看下文章被的这些事例,相信对君要非常有扶持的。在就学上的投资会叫您将来受益无穷。我意信任,在未来,JS中那些复杂的异步能力将起源于我此的部分想方设法。

 

2. ThreadLocal

ThreadLocal位于java.lang包中,以下是JDK文档中针对此类的讲述

Implements a thread-local storage, that is, a variable for which each
thread has its own value. All threads share the same ThreadLocal object,
but each sees a different value when accessing it, and changes made by
one thread do not affect the other threads. The implementation supports
null values.

大体意思是,ThreadLocal实现了线程本地存储。所有线程共享同一个ThreadLocal对象,但不同线程仅能够访问同那线程相关联的值,一个线程修改ThreadLocal对象对其他线程没有影响。

ThreadLocal为编写多线程并发程序提供了一个初的笔触。如下图所示,我们得以以ThreadLocal理解吧同块存储区,将随即同样充分块存储区分割为多片小之存储区,每一个线程拥有同等片属于自己的存储区,那么对友好之存储区操作就非见面影响其他线程。对于ThreadLocal<Looper>,则诸一样微片存储区中不怕保留了和特定线程关联的Looper。
葡京娱乐总站平台 1

CSP(Communicating Sequential Processes)

  首先,我形容就无异多级文章意是深受Nolen
@swannodette优质工作之诱导。说确实,他写的富有文章还值得去念一朗诵。我这里来一对链接可以大快朵颐给您:

  • Communicating Sequential
    Processes
  • ES6 Generators Deliver Go Style
    Concurrency
  • Extracting
    Processes

  好了,让咱规范开班对这个主题的探讨。我无是一个打具有Clojure(Clojure凡是同样种植运行于Java平台上之
Lisp
方言)背景转投到JS阵营的程序员,而且自哉未曾另外Go或者ClojureScript的更。我发现自己在念这些文章的时光快便会去兴趣,因此我不得不做过多之尝试并从中了解及有的灵光的事物。

  以这历程遭到,我认为自己早已生矣有些如出一辙之思辨,并追一致的靶子,而这些都源自于一个勿那么死板的思量方式。

  我尝试创建了一个重新简便易行的Go风格的CSP(以及ClojureScript
core.async)APIs,同时自身望会保存大部分的平底功能。也许有大神会看到我文章被落的地方,这全然有或。如果算这样的话,我期望自己的探讨能够收获更的前进以及演化,而己哉拿与豪门齐声来分享者历程!

 

3. ThreadLocal的内部贯彻原理

详解CSP原理(一点点)

  到底什么是CSP?说它们是”communicating”,”Sequential”,”processes”到底是啊意思呢?

  首先,CSP一词源自于Tony Hoare所著的“Communicating Sequential
Processes”一修。里面都是关于CS的论战,如果您对学术方面的事物感兴趣的话,这仍开纯属值得一念。我毫无打算为同一种植于人难以理解的,深奥的,计算机对的道来阐述是主题,而是会为同一种植轻松的非正式的不二法门来拓展。

  那咱们尽管起”Sequential”开始吧!这片公应该已经颇熟悉了。这是另外一栽谈论有关单线程和ES6
generators异步风格代码的道。我们来回顾一下generators的语法:

function *main() {
    var x = yield 1;
    var y = yield x;
    var z = yield (y * 2);
}

  上面代码中的各个一样条告句子都见面仍顺序一个一个地履。Yield要字标明了代码中叫打断的触及(只能被generator函数自己死,外部代码不克围堵generator函数的行),但是未见面改变*main()函数中代码的执行各个。这段代码很简单!

  接下我们来讨论一下”processes”。这个是呀吧?

  基本上,generator函数有点像一个虚拟的”process”,它是咱先后的一个独门的有,如果JavaScript允许,它完全好同程序的另一些并行执行。这听起来似乎来三三两两荒唐!如果generator函数访问共享内存(即,如果它访问除了自己之中定义之组成部分变量之外的“自由变量”),那么它们就未是一个独立的有些。现在咱们借设有一个休聘外部变量的generator函数(在FP(Functional
Programming函数式编程)的争辩中我们将它叫做一个”combinator”),因此从理论及吧她好于自己的process中运行,或者说当自己之process来运转。

  但是咱说之是”processes”,注意是单词用底凡复数,这是以会是个别独或多独process在同一时间运行。换句话说,两个或多单generators函数会吃放一起来协同工作,通常是以成功同样起于充分的任务。

  为什么要用几近只单身的generator函数,而非是管其都坐一个generator函数里呢?一个极度关键的原故就是是:效能以及关注点的分手。对于一个职责XYZ来说,如果您用她说成子任务X,Y和Z,那么以每个子任务协调之generator函数中来实现效益以见面要代码更易于了解以及维护。这同用函数XYZ()拆分成X()Y(),和Z(),然后在X()中调用Y(),在Y()中调用Z()举凡同的道理。我们以函数分解成一个个独的子函数,降低代码的耦合度,从而使程序更为便于保障。

3.1 Thread、ThreadLocal和Values的关系

Thread的积极分子变量localValues代表了线程特定变量,类型也ThreadLocal.Values。由于线程特定变量可能会见产生差不多只,并且类型不确定,所以ThreadLocal.Values有一个table成员变量,类型也Object数组。这个localValues可以清楚啊二维存储区中同一定线程相关的一列。
ThreadLocal类则一定给一个摄,真正操作线程特定存储区table的凡那中间类Values。
葡京娱乐总站平台 2
葡京娱乐总站平台 3

对多独generators函数来说我们为足以做到即或多或少

  这将说交”communicating”了。这个以是什么啊?就是协作。如果我们拿多个generators函数放在有协同工作,它们彼此之间需要一个通信信道(不仅仅是访问共享的作用域,而是一个实在的足吃其访问的独占式共享通信信道)。这个通信信道是什么啊?不管您发送什么内容(数字,字符串等),事实上你都非欲通过信道发送信息来进行通信。通信会像合作那样简单,就像以先后的控制权从一个地方转移至另外一个地方。

  为什么要转移控制?这要是坐JS是单线程的,意思是说在随心所欲给定的一个日子有些外只会起一个主次在运转,而其他程序都处在暂停状态。也就是说其它程序还地处它们分别职责之中间状态,不过就是为中止实施,必要常常会见回复并持续运行。

  任意独立的”processes”之间可以神奇地进行通信与搭档,这任起有些不因谱。这种解耦的想法是好之,但是出接触不切实际。相反,似乎其他一个遂之CSP的兑现还是针对性那些问题领域面临一度是的、众所周知的逻辑集的特有说,其中每个有都叫突出设计了用令各个片中还能够美工作。

  或许自己的明亮了是错的,但是自还未曾观看任何一个切实的点子,能够吃个别只随机给定的generator函数可以以某种方式随机地集合合在一起形成CSP对。它们还用让规划成为能与其余一些联合坐班,需要以彼此间的通信协议等等。

 

3.2 set方法

public void set(T value) {
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values == null) {
        values = initializeValues(currentThread);
    }
    values.put(this, value);
}

Values values(Thread current) {
    return current.localValues;
}

既然跟一定线程相关,所以先取得当前线程,然后取当前线程特定存储,即Thread中之localValues,若localValues为空,则开创一个,最后将value存入values中。

void put(ThreadLocal<?> key, Object value) {
    cleanUp();

    // Keep track of first tombstone. That's where we want to go back
    // and add an entry if necessary.
    int firstTombstone = -1;

    for (int index = key.hash & mask;; index = next(index)) {
        Object k = table[index];

        if (k == key.reference) {
            // Replace existing entry.
            table[index + 1] = value;
            return;
        }

        if (k == null) {
            if (firstTombstone == -1) {
                // Fill in null slot.
                table[index] = key.reference;
                table[index + 1] = value;
                size++;
                return;
            }

            // Go back and replace first tombstone.
            table[firstTombstone] = key.reference;
            table[firstTombstone + 1] = value;
            tombstones--;
            size++;
            return;
        }

        // Remember first tombstone.
        if (firstTombstone == -1 && k == TOMBSTONE) {
            firstTombstone = index;
        }
    }
}

起put方法被,ThreadLocal的reference和价值都见面满怀进table,索引分别吗index和index+1。
对Looper这个例子,
table[index] = sThreadLocal.reference;(指向自己的一个毙命引用)
table[index + 1] = 与当前线程关联的Looper

JS中的CSP

  在拿CSP的争鸣运用至JS中,有有万分有意思的探讨。前面提到的David
Nolen,他来几只大有趣的品类,包括Om,以及core.async。Koa库房(node.js)主要透过它们的use(..)术体现了立即或多或少。而另外一个对core.async/Go
CSP API十分忠于的堆栈是js-csp。

  你实在当去看看这些伟人的类,看看其中的各种法子以及例子,了解它们是哪以JS中落实CSP的。

 

3.3 get方法

public T get() {
    // Optimized for the fast path.
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values != null) {
        Object[] table = values.table;
        int index = hash & values.mask;
        if (this.reference == table[index]) {
            return (T) table[index + 1];
        }
    } else {
        values = initializeValues(currentThread);
    }

    return (T) values.getAfterMiss(this);
}

先是取出与线程相关的Values,然后在table中找寻ThreadLocal的reference对象在table中的职务,然后回下一个职位所蕴藏的目标,即ThreadLocal的价,在Looper这个例子中就是与当下线程关联的Looper对象。

打set和get方法好望,其所操作的都是眼下线程的localValues中之table数组,所以不同线程调用同一个ThreadLocal对象的set和get方法互不影响,这即是ThreadLocal为化解多线程程序的面世问题提供了一样种新的思绪。

异步的runner(..):设计CSP

  因为我一直以尽力探索用相互的CSP模式应用及自家自己之JS代码中,所以对于以CSP来扩充自己好的异步流程控制库asynquence来说即使是如出一辙件顺理成章的事。我形容了的runner(..)插件(看上一篇稿子:ES6
Generators的异步应用)就是用来拍卖generators函数的异步运行的,我发现它们好生容易为扩大用来拍卖多generators函数在同一时间运行,纵使比如CSP的法子那样。

  我若缓解的首先单计划问题是:如何才会分晓哪个generator函数将取得下一个控制权?

  要化解各个generators函数之间的音讯还是控制权的传递,每个generator函数都得持有一个克于另外generators函数知道的ID,这看起如过于笨拙。经过各种尝试,我设定了一个简约的轮回调度方式。如果你配合了三独generators函数A,B和C,那么A将先行获控制权,当A
yield时B将接管A的控制权,然后当B yield时C将接管B,然后还要是A,以此类推。

  但是怎么才能够实际转移generator函数的控制权也?应该出一个显式的API吗?我再也进行了各种尝试,然后设定了一个进一步隐式的方式,看起与Koa有接触类似(完全是外):每个generator函数都取一个共享”token”的援,当yield时虽代表如果拿控制权进行换。

  另一个问题是信息通道应该加上什么样。一种植是不行标准的通信API如core.async和js-csp(put(..)take(..))。但是于我透过各种尝试后,我较支持于任何一样种植不顶正统的不二法门(甚至还讲不达标API,而光是一个共享的数据结构,例如数组),它看起像是于靠谱的。

  我操运用数组(称之为消息),你得依据需要控制哪些填写和清空数组的内容。你可以push()信息及数组中,从数组中pop()信息,按照预约将不同的音信存放到数组中一定的职务,并在这些位置存放更扑朔迷离的数据结构等。

  我的困惑是产生来任务要传递简单的消息,而稍则要传递复杂的信息,因此不要当局部简便的情景下强制这种复杂度,我选不拘泥于信息通道的款型要动数组(除数组自己他这里没有另外API)。在一些情况下其充分爱在附加的款型上对消息传递机制进行分,这对准我们的话非常有因此(参见下的状态机示例)。

  最终,我发觉这些generator
“processes”仍然得益于那些独自的generators可以行使的异步功能。也就是说,如果未yield控制token,而yield一个Promise(或者一个异步队列),则runner(..)的确会暂停为待返回值,不过不见面转移控制权,它见面以结果返回给当下之process(generator)而保留控制权。

  最后一点也许是绝有争执或同本文中其他库差别最可怜之(如果我讲是的语)。也许的确的CSP对这些艺术不屑一顾,但是本人发觉我的选取还是生有因此底。

 

4. ThreadLocal背后底筹划思想Thread-Specific Storage模式

Thread-Specific
Storage让大多独线程能够用同一之”逻辑全局“访问点来取线程本地的目标,避免了历次看对象的锁定开销。

一个傻乎乎的FooBar示例

  好了,理论的事物摆得多了。我们来探现实的代码:

// 注意:为了简洁,省略了虚构的`multBy20(..)`和`addTo2(..)`异步数学函数

function *foo(token) {
    // 从通道的顶部获取消息
    var value = token.messages.pop(); // 2

    // 将另一个消息存入通道
    // `multBy20(..)`是一个promise-generating函数,它会延迟返回给定值乘以`20`的计算结果
    token.messages.push( yield multBy20( value ) );

    // 转移控制权
    yield token;

    // 从CSP运行中的最后的消息
    yield "meaning of life: " + token.messages[0];
}

function *bar(token) {
    // 从通道的顶部获取消息
    var value = token.messages.pop(); // 40

    // 将另一个消息存入通道
    // `addTo2(..)` 是一个promise-generating函数,它会延迟返回给定值加上`2`的计算结果
    token.messages.push( yield addTo2( value ) );

    // 转移控制权
    yield token;
}

  上面的代码中生出三三两两只generator
“processes”,*foo()*bar()。它们还接并拍卖一个令牌(当然,如果你愿意你得随心所欲吃什么都施行)。令牌直达之性质messages不畏是咱的共享信息通道,当CSP运行时她会落初始化传入的消息值进行填写(后面会称到)。

  yield
token
显式地以控制权转移到“下一个”generator函数(循环顺序)。但是,yield
multBy20(value)
yield
addTo2(value)
还是yield一个promises(从即简单只虚构的推计算函数中归的),这意味着generator函数此时凡处于停顿状态直到promise完成。一旦promise完成,当前处控制中之generator函数会死灰复燃并蝉联运行。

  无论最后yield会晤回到什么,上面的例证中yield返回的是一个表达式,都代表我们的CSP运行完成的信息(见下文)。

  现在咱们发出零星只CSP process
generators,我们来探望如何运转它们?使用asynquence:

// 开始一个sequence,初始message的值是2
ASQ( 2 )

// 将两个CSP processes进行配对一起运行
.runner(
    foo,
    bar
)

// 无论接收到的message是什么,都将它传入sequence中的下一步
.val( function(msg){
    console.log( msg ); // 最终返回42
} );

  这只是是一个怪简单的事例,但自我当她会大好地用来诠释上面的这些概念。你可以尝试一下(试着改变一些值),这有助于你明白这些概念并协调动手编写代码!

 

4.1 Thread-Specific Storage模式的来源于

errno机制被普遍用于一些操作系统平台。errno
是记录系统的终极一糟糕错误代码。对于单线程程序,在大局意图域内实现errno的功用是,但在差不多线程操作系统中,多线程并发可能致一个线程设置的errno值被外线程错误解读。当时广大遗留库和应用程序都是因单线程编写,为了以未改既出接口及遗留代码的事态下,解决多线程访问errno的题目,Thread-Specific
Storage模式诞生。

外一个事例Toy Demo

  让咱来拘禁一个藏的CSP例子,但只是从我们眼前就部分有简约的发现开始,而未是于我们司空见惯所说之纯学术的角度来展开讨论。

  Ping-pong。一个非常有意思之玩耍,对啊?也是本人尽欣赏的位移。

  让我们来设想一下若曾经形成了这乒乓球游戏之代码,你通过一个巡回来运转游戏,然后出个别有些代码(例如当ifswitch告句被之分支),每一样局部代表一个相应的玩家。代码运行正常,你的玩耍运行起来就是比如是一个乒乓球冠军!

  但是本我们地方讨论过之,CSP在此间打及了哪些的意向为?哪怕是法力以及关注点的诀别。那么具体到我们的乒乓球游戏中,这个分离指的就是是少个不同之玩家

  那么,我们得以以一个特别强的范围达到之所以鲜只”processes”(generators)来法我们的游艺,每个玩家一个”process”。当我们落实代码细节之时,我们见面发现于少数单玩家的寒在操纵的切换,我们叫”glue
code”(胶水代码(译:在处理器编程领域,胶水代码也受粘合代码,用途是贴那些或未般配的代码。可以使与胶合在一起的代码相同的语言编写,也可就此单独的胶水语言编写。胶水代码不实现程序要求的另外力量,它通常出现在代码中,使现有的堆栈或者程序于表函数接口(如Java本地接口)中开展互操作。胶水代码在快捷原型开发条件遭到颇快,可以叫几乎独零件为快速集成及单个语言还是框架中。)),这个职责自我可能需要第三只generator的代码,我们好拿其套成游戏的裁判

  我们打算过了各种特定领域的问题,如计分、游戏机制、物理原理、游戏策略、人工智能、操作控制等。这里我们唯一用关爱的部分就是是仿照打乒乓球的来回过程(这其实也代表了俺们CSP的操纵转移)。

  想看demo的言语可于这里运作(注意:在支持ES6
JavaScript的新式版本的FireFox
nightly或Chrome中查看generators是怎么样行事的)。现在,让我们一块来瞧代码。首先,来探asynquence
sequence长什么样?

ASQ(
    ["ping","pong"], // 玩家姓名
    { hits: 0 } // 球
)
.runner(
    referee,
    player,
    player
)
.val( function(msg){
    message( "referee", msg );

  我们初始化了一个messages sequence:[“ping”, “pong”]{hits:
0}
。一会儿会晤就此到。然后,我们安了一个分包3单processes运行的CSP(相互协同工作):一个*referee()和两个*player()实例。在打闹了时最后的message会被传送让sequence中的生一样步,作为referee的输出message。下面是referee的贯彻代码:

function *referee(table){
    var alarm = false;

    // referee通过秒表(10秒)为游戏设置了一个计时器
    setTimeout( function(){ alarm = true; }, 10000 );

    // 当计时器警报响起时游戏停止
    while (!alarm) {
        // 玩家继续游戏
        yield table;
    }

    // 通知玩家游戏已结束
    table.messages[2] = "CLOSED";

    // 裁判宣布时间到了
    yield "Time's up!";
}
} );

  这里我们就此table来拟控制令牌以解决我们地方说之那些特定领域的问题,这样即使能够挺好地来叙述当一个玩家将球打回去的时候控制权被yield给任何一个玩家。*referee()中的while循环表示如果秒表没有停,程序就算会见一直yield
table
(将控制权转移给任何一个玩家)。当计时器结束时离while循环,referee将会见接管控制权并发布”Time’s
up!
“游戏结束了。

  再来看望*player() generator的实现代码(我们用有限单实例):

function *player(table) {
    var name = table.messages[0].shift();
    var ball = table.messages[1];

    while (table.messages[2] !== "CLOSED") {
        // 击球
        ball.hits++;
        message( name, ball.hits );

        // 模拟将球打回给另一个玩家中间的延迟
        yield ASQ.after( 500 );

        // 游戏继续?
        if (table.messages[2] !== "CLOSED") {
            // 球现在回到另一个玩家那里
            yield table;
        }
    }

    message( name, "Game over!" );
}

  第一独玩家用他的讳起message数组的第一单因素被移除(”ping“),然后第二只玩家获取他的讳(”pong“),以便他们还能够正确地辨认自己(译:注意这里是片只*player()的实例,在简单个不等之实例中,通过table.messages[0].shift()可得到各自不同之玩家名字)。同时少独玩家还保持对共享球的引用(使用hits计数器)。

  当玩家还尚未听到判决说罢,就“击球”并累加计数器(并出口一个message来通知其),然后等待500毫秒(假设球盖光速运行不占其他时刻)。如果玩还以持续,他们便yield
table到任何一个玩家那里。就是这么。

  在这里得查完代码,从而了解代码的各级部分是怎样工作的。

 

4.2 Thread-Specific Storage模式的共同体组织

葡京娱乐总站平台 4

线程特定对象,相当于Looper。
线程特定目标集含蓄一组及特定线程相关联的线程特定目标。每个线程都有协调的线程特定对象集。相当给ThreadLocal.Values。线程特定目标集可以储存在线程内部或外部。Win32、Pthread和Java都指向线程特定数据发生支持,这种情景下线程特定目标集可以储存于线程内部。
线程特定对象代理,吃客户端能够像看常规对象同看线程特定对象。如果没代理,客户端必须一直看线程特定目标集并出示地使用键。相当给ThreadLocal<Looper>。

起概念上谈,可拿Thread-Specific
Storage的组织即一个二维矩阵,每个键对应一行,每个线程对应一列。第k履、第t排的矩阵元素也对相应线程特定目标的指针。线程特定对象代理及线程特定目标集协作,向应用程序线程提供相同栽访问第k实行、第t排列对象的平安机制。注意,这个模型只是类比。实际上Thread-Specific
Storage模式的落实并无是下二维矩阵,因为键不必然是隔壁整数。

葡京娱乐总站平台 5

 

  

 

状态机:Generator协同程序

  最后一个例子:将一个状态机概念为由一个简易的helper驱动的相同组generator协同程序。Demo(注意:在支撑ES6
JavaScript的时版本的FireFox
nightly或Chrome中查阅generators是安工作之)。

  首先,我们定义一个helper来控制有限的状态处理程序。

function state(val,handler) {
    // 管理状态的协同处理程序(包装器)
    return function*(token) {
        // 状态转换处理程序
        function transition(to) {
            token.messages[0] = to;
        }

        // 默认初始状态(如果还没有设置)
        if (token.messages.length < 1) {
            token.messages[0] = val;
        }

        // 继续运行直到最终的状态为true
        while (token.messages[0] !== false) {
            // 判断当前状态是否和处理程序匹配
            if (token.messages[0] === val) {
                // 委托给状态处理程序
                yield *handler( transition );
            }

            // 将控制权转移给另一个状态处理程序
            if (token.messages[0] !== false) {
                yield token;
            }
        }
    };
}

  state(..)
helper为特定的状态值创造了一个delegating-generator包装器,这个包裹器会自动运行状态机,并在每个状态切换时换控制权。

  依照惯例,我控制采用共享token.messages[0]的职位来保存我们状态机的眼前状态。这象征你可经打序列中前一模一样步传的message来设定初始状态。但是如果无传到初始值的语句,我们见面略地将首先个状态作为默认的初始值。同样,依照惯例,最终之状态会叫如为false。这万分轻改以可你协调的要。

  状态值可以是外你想使的价:numbersstrings抵。只要该值可以为===运算符严格测试通过,你便足以应用她看做你的状态。

  在下面的示范中,我展示了一个状态机,它可随一定的各个以四独数值状态中开展转移:1->4->3->2。为了演示,这里运用了一个计数器,因此好实现多次巡回转换。当我们的generator状态机到达最终状态时(false),asynquence序列就会见如而所欲的那么倒到下一样步。

// 计数器(仅用作演示)
var counter = 0;

ASQ( /* 可选:初始状态值 */ )

// 运行状态机,转换顺序:1 -> 4 -> 3 -> 2
.runner(

    // 状态`1`处理程序
    state( 1, function*(transition){
        console.log( "in state 1" );
        yield ASQ.after( 1000 ); // 暂停1s
        yield transition( 4 ); // 跳到状态`4`
    } ),

    // 状态`2`处理程序
    state( 2, function*(transition){
        console.log( "in state 2" );
        yield ASQ.after( 1000 ); // 暂停1s

        // 仅用作演示,在状态循环中保持运行
        if (++counter < 2) {
            yield transition( 1 ); // 跳转到状态`1`
        }
        // 全部完成!
        else {
            yield "That's all folks!";
            yield transition( false ); // 跳转到最终状态
        }
    } ),

    // 状态`3`处理程序
    state( 3, function*(transition){
        console.log( "in state 3" );
        yield ASQ.after( 1000 ); // 暂停1s
        yield transition( 2 ); // 跳转到状态`2`
    } ),

    // 状态`4`处理程序
    state( 4, function*(transition){
        console.log( "in state 4" );
        yield ASQ.after( 1000 ); // 暂停1s
        yield transition( 3 ); // 跳转到状态`3`
    } )

)

// 状态机完成,移动到下一步
.val(function(msg){
    console.log( msg );
});

  应该挺爱地跟踪点的代码来查看究竟有了什么。yield
ASQ.after(1000)
显了这些generators可以依据需要做任何项目的因promise/sequence的异步工作,就比如我们在前面所观看底同。yield
transition(…)
意味着什么更换到一个初的状态。上面代码中的state(..)
helper完成了处理yield*
delegation以及状态转换的重要性办事,然后一切程序的最主要流程看起特别略,表述为够呛鲜明流利。

 

总结

  CSP的主要是拿鲜独或再多之generator
“processes”连接于共,给它一个共享的通信信道,以及同样栽好于互相间传输控制的艺术。

  JS中有成千上万之库都或多还是丢失地利用了一对一专业的办法来和Go和Clojure/ClojureScript
APIs或语义相匹配。这些库底暗都兼备很深的开发者,对于更为探讨CSP来说他们都是那个好的资源。

  asynquence计较利用相同种植不太标准而而盼还是能保留重要布局的措施。如果无别的
,asynquence的runner(..)好当做你尝试与习CSP-like
generators的入门。

  最好之有的是asynquence
CSP与其余异步功能(promises,generators,流程控制等)在一起工作。如此一来,你便可掌控一切,使用任何你手头上妥的家伙来成功任务,而具备的及时所有都单以一个小小的lib中。

  现在我们已当就四篇稿子中详尽探索了generators,我期望你能够从中受益并获灵感以探讨如何改革自己的异步JS代码!你拿据此generators来创造什么啊?

 

原稿地址:https://davidwalsh.name/es6-generators