<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>异步 on 首页 | Widegram</title>
    <link>https://widegram.com/categories/%E5%BC%82%E6%AD%A5/</link>
    <description>Recent content in 异步 on 首页 | Widegram</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Tue, 09 Nov 2021 21:19:00 +0000</lastBuildDate><atom:link href="https://widegram.com/categories/%E5%BC%82%E6%AD%A5/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Flutter 异步全解</title>
      <link>https://widegram.com/2021/11/flutter-async/</link>
      <pubDate>Tue, 09 Nov 2021 21:19:00 +0000</pubDate>
      
      <guid>https://widegram.com/2021/11/flutter-async/</guid>
      <description>一、异步入门 1.1 async和await 这两个关键词的本质是语法糖。例如：
void _increment() async { await Future.delayed(Duration(seconds: 1)); setState((){ _counter++; }); } 等价于：
void _increment() { Future.delayed(Duration(seconds: 1),(){ setState((){ _counter++; }); }); } 或者：
void _increment() { Future.delayed(Duration(seconds: 1)).then( (value) =&amp;gt; setState((){ _counter++; }) ); } ​	这里需要纠正一个误区：就像JS那样，dart的异步操作并非多线程。在dart中，每一个线程都被isolate包裹，它们之间不能共享内存，也就避免了死锁问题，降低了程序复杂度，提升GC的性能。线程之间使用消息队列来通信。
​	一个线程怎么处理等待事件呢？这里就要用到Event Loop机制。内置一个Event Queue和MicroTask Queue，只要MicroTask Queue中还有事件没有处理完，则先处理这里面的事件，而此时EventQueue中的事件一个都不会被执行。使用scheduleMicrotask()来向Microtask Queue添加事件。
void main(){ Future(() =&amp;gt; print(&amp;#39;A&amp;#39;)); print(&amp;#39;B&amp;#39;); } //打印结果：BA ​	这里我们还是要提醒一下，只有在出现需要等待的事件时，EventLoop机制才能发挥作用。如果卡顿是因为计算量过大造成的，那么只有多开几个Isolate（线程）来解决了。
1.2 运行时机 直接运行：Future.sync()、Future.value()、_.then()
Microtask：scheduleMicrotask()、Future.microtask()、_completed.then()
Event：Future()、Future.delayed()
1.3 运行时机举例 直接运行
void main(){ print(&amp;#34;1&amp;#34;); Future.value(func()); print(&amp;#34;3&amp;#34;); } String func(){ print(&amp;#34;2&amp;#34;); return &amp;#34;&amp;#34;; }//输出结果：123 void main(){ print(&amp;#34;1&amp;#34;); Future.</description>
      <content:encoded><![CDATA[<h2 id="一异步入门">一、异步入门</h2>
<h4 id="11-async和await">1.1 async和await</h4>
<p>这两个关键词的本质是语法糖。例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> _increment() <span style="color:#66d9ef">async</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">await</span> Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>));
</span></span><span style="display:flex;"><span>  setState((){ _counter<span style="color:#f92672">++</span>; });
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>等价于：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> _increment() {
</span></span><span style="display:flex;"><span>  Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>),(){
</span></span><span style="display:flex;"><span>    setState((){ _counter<span style="color:#f92672">++</span>; });
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>或者：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> _increment() {
</span></span><span style="display:flex;"><span>  Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>)).then(
</span></span><span style="display:flex;"><span>  	(value) <span style="color:#f92672">=&gt;</span> setState((){ _counter<span style="color:#f92672">++</span>; })
</span></span><span style="display:flex;"><span>  );
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>​		这里需要纠正一个误区：就像JS那样，dart的异步操作并非多线程。在dart中，每一个线程都被isolate包裹，它们之间不能共享内存，也就避免了死锁问题，降低了程序复杂度，提升GC的性能。线程之间使用消息队列来通信。</p>
<p>​		一个线程怎么处理等待事件呢？这里就要用到Event Loop机制。内置一个Event Queue和MicroTask Queue，只要MicroTask Queue中还有事件没有处理完，则先处理这里面的事件，而此时EventQueue中的事件一个都不会被执行。使用<code>scheduleMicrotask()</code>来向Microtask Queue添加事件。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main(){
</span></span><span style="display:flex;"><span>  Future(() <span style="color:#f92672">=&gt;</span> print(<span style="color:#e6db74">&#39;A&#39;</span>));
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#39;B&#39;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//打印结果：BA
</span></span></span></code></pre></div><p>​		这里我们还是要提醒一下，只有在出现需要等待的事件时，EventLoop机制才能发挥作用。如果卡顿是因为计算量过大造成的，那么只有多开几个Isolate（线程）来解决了。</p>
<h4 id="12-运行时机">1.2 运行时机</h4>
<ol>
<li>
<p>直接运行：<code>Future.sync()</code>、<code>Future.value()</code>、<code>_.then()</code></p>
</li>
<li>
<p>Microtask：<code>scheduleMicrotask()</code>、<code>Future.microtask()</code>、<code>_completed.then()</code></p>
</li>
<li>
<p>Event：<code>Future()</code>、<code>Future.delayed()</code></p>
</li>
</ol>
<h4 id="13-运行时机举例">1.3 运行时机举例</h4>
<ol>
<li>
<p>直接运行</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main(){
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;1&#34;</span>);
</span></span><span style="display:flex;"><span>  Future.value(func());
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;3&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">String</span> func(){
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;2&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;&#34;</span>;
</span></span><span style="display:flex;"><span>}<span style="color:#75715e">//输出结果：123
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main(){
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;1&#34;</span>);
</span></span><span style="display:flex;"><span>  Future.<span style="color:#66d9ef">sync</span>(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;2&#34;</span>));
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;3&#34;</span>);
</span></span><span style="display:flex;"><span>}<span style="color:#75715e">//输出结果：123
</span></span></span></code></pre></div></li>
<li>
<p>Event：</p>
<p>以http请求来说：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>http.<span style="color:#66d9ef">get</span>(<span style="color:#e6db74">&#39;www.baidu.com&#39;</span>)
</span></span><span style="display:flex;"><span>  .then((value)<span style="color:#f92672">=&gt;</span>{<span style="color:#66d9ef">null</span>})
</span></span><span style="display:flex;"><span>  .catchError(...);	<span style="color:#75715e">//返回一个Future
</span></span></span></code></pre></div><p>函数返回Future，这里看起来Future是立刻执行的，但实际上要在下一个循环中才会被执行：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Future<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">String</span><span style="color:#f92672">&gt;</span> getFuture(){
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> Future(() <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;alice&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main(){
</span></span><span style="display:flex;"><span>  getFuture().then((value)<span style="color:#f92672">=&gt;</span>print(value));
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;hi&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//打印结果：hi alice
</span></span></span></code></pre></div></li>
</ol>
<p>来个大汇总吧：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main(){
</span></span><span style="display:flex;"><span>  Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>),() <span style="color:#f92672">=&gt;</span> print(<span style="color:#e6db74">&#34;event 3&#34;</span>));
</span></span><span style="display:flex;"><span>  Future.delayed(Duration.zero, ()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;event 2&#34;</span>));
</span></span><span style="display:flex;"><span>  Future(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;event 1&#34;</span>));<span style="color:#75715e">//这里的event 1与event 2等价，调换语句顺序，执行顺序也会对应调换
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  
</span></span><span style="display:flex;"><span>  scheduleMicrotask(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;mtask 1&#34;</span>));
</span></span><span style="display:flex;"><span>  Future.microtask(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;mtask 2&#34;</span>));
</span></span><span style="display:flex;"><span>  Future.value(<span style="color:#ae81ff">123</span>).then((value)<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;mtask 3&#34;</span>));	<span style="color:#75715e">//_completed.then()
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;main 1&#34;</span>);
</span></span><span style="display:flex;"><span>  Future.<span style="color:#66d9ef">sync</span>(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;sync 1&#34;</span>));
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;main 2&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">/*
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">	输出结果：
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		mtask 1
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		mtask 2
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		mtask 3
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		event 2
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		event 1		
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		event 3
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		main 1
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		sync 1
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		main 2
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">*/</span>
</span></span></code></pre></div><p>最后落下一个会立即执行的<code>_.then()</code>：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>),() <span style="color:#f92672">=&gt;</span> print(<span style="color:#e6db74">&#34;delayed&#34;</span>))
</span></span><span style="display:flex;"><span>	.then((value){
</span></span><span style="display:flex;"><span>    scheduleMicrotask(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;micro&#34;</span>));
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;then&#34;</span>);
</span></span><span style="display:flex;"><span>  }).then((value)<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;then 2&#34;</span>));
</span></span><span style="display:flex;"><span><span style="color:#75715e">//结果：delayed then then2 micro
</span></span></span></code></pre></div><h4 id="14-error和whencomplete">1.4 error和whenComplete</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>getFuture()
</span></span><span style="display:flex;"><span>  .then((value)<span style="color:#f92672">=&gt;</span>print(value))
</span></span><span style="display:flex;"><span>  .catchError((err)<span style="color:#f92672">=&gt;</span>print(err))
</span></span><span style="display:flex;"><span>  .whenComplete(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;complete&#34;</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Future<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">String</span><span style="color:#f92672">&gt;</span> getFuture(){
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> Future.error(Exception(<span style="color:#e6db74">&#34;wrong&#34;</span>));
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//wrong complete
</span></span></span></code></pre></div><h2 id="二builder和controller">二、Builder和Controller</h2>
<h4 id="21-futurebuilder">2.1 FutureBuilder</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>body: FutureBuilder(
</span></span><span style="display:flex;"><span>	future: Future.value(<span style="color:#ae81ff">123</span>),
</span></span><span style="display:flex;"><span>  builder: (context, AsyncSnapshot<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">dynamic</span><span style="color:#f92672">&gt;</span> snapshot){
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//snapshot代表最近的状态：未完成、完成、出错
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">if</span>(snapshot.connectionState <span style="color:#f92672">==</span> ConnectionState.waiting){
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">return</span> CircularProgressIndicator();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">if</span>(snapshot.connectionState <span style="color:#f92672">==</span> ConnectionState.done){
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span>(snapshot.hasError){
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> Text(<span style="color:#e6db74">&#34;error&#34;</span>);
</span></span><span style="display:flex;"><span>      } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> Text(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>snapshot.data<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>);	<span style="color:#75715e">//data和error只有一个有数据
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//none代表前面设置了future: null，不可能发生；而active设计Stream，暂不考虑
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>   	<span style="color:#66d9ef">throw</span> <span style="color:#e6db74">&#34;should not happen&#34;</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span><span style="color:#75715e">//更加可读
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>body: FutureBuilder(
</span></span><span style="display:flex;"><span>	future: Future.value(<span style="color:#ae81ff">123</span>),
</span></span><span style="display:flex;"><span>  initialData: <span style="color:#ae81ff">72</span>,	<span style="color:#75715e">//未完成时snapshot.data = 72
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  builder: (context, AsyncSnapshot<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">dynamic</span><span style="color:#f92672">&gt;</span> snapshot){
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span>(snapshot.hasError){
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">return</span> Icon(Icons.error, size:<span style="color:#ae81ff">80</span>);
</span></span><span style="display:flex;"><span>    } <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">if</span>(snapshot.hasData){
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">return</span> Text(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>snapshot.data<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>    } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">return</span> CircularProgressIndicator();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>​		值得一提的是，snapshot中的error和data属性只能有一个存在值，如果出错，flutter会先删除原来data中的initialData，再填上对应的error。</p>
<h4 id="22-stream和streambuilder">2.2 Stream和StreamBuilder</h4>
<p>Future负责在以后给我们一个值，而stream则会在以后给我们很多值。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">final</span> future <span style="color:#f92672">=</span> Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>), ()<span style="color:#f92672">=&gt;</span><span style="color:#ae81ff">42</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">final</span> stream <span style="color:#f92672">=</span> Stream.periodic(Duration(seconds: <span style="color:#ae81ff">1</span>), (_) <span style="color:#f92672">=&gt;</span> <span style="color:#ae81ff">42</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> initialState(){
</span></span><span style="display:flex;"><span>  future.then(...);
</span></span><span style="display:flex;"><span>  stream.listen((event){
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;stream: </span><span style="color:#e6db74">$</span>event<span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
</span></span></code></pre></div><p>可以使用StreamBuilder监控stream：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>body: StreamBuilder(
</span></span><span style="display:flex;"><span>	stream: stream,
</span></span><span style="display:flex;"><span>  builder: (context, AsyncSnapshot<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">dynamic</span><span style="color:#f92672">&gt;</span> snapshot) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//可以使用switch来判断snapshot.connectionState
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>​		Stream和Future的区别之一就在于，Future是在结束（done）的时候返回一个data或error，而Stream是在active的状态返回一个data或error，而stream一旦处于done状态，那么向stream发送数据就会引发异常。</p>
<h4 id="23-streamcontroller">2.3 StreamController</h4>
<p>形象地说，Stream就像水流，sink就像水龙头（数据来源），StreamController会同时创建这两者：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">final</span> controller <span style="color:#f92672">=</span> StreamController();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>controller.sink.add(<span style="color:#ae81ff">72</span>);<span style="color:#75715e">//添加一个值，数据类型可以混搭
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>controller.sink.addError(<span style="color:#e6db74">&#34;error&#34;</span>);<span style="color:#75715e">//添加错误
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>controller.stream.listen((event) {  });<span style="color:#75715e">//处理事件
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span>controller.close();<span style="color:#75715e">//关闭流
</span></span></span></code></pre></div><p>使用StreamBuilder：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>body: StreamBuilder(
</span></span><span style="display:flex;"><span>	stream: controller.stream,		<span style="color:#75715e">//监听controller
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  builder: (context, AsyncSnapshot<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">dynamic</span><span style="color:#f92672">&gt;</span> snapshot) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//可以使用switch来判断snapshot.connectionState
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><h4 id="24-streambroadcast">2.4 Stream.broadcast()</h4>
<p>​		一般来说，一个Stream只能被一方监听，同时未被接受的数据会被缓存；反过来说，使用<code>Stream.broadcast</code>创建的广播流可以被多方监听，但未被接受的数据不再会被缓存。</p>
<h2 id="三stream高级操作">三、Stream高级操作</h2>
<h4 id="31-streammap">3.1 Stream.map</h4>
<p>例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>stream: controller.stream.map((event) <span style="color:#f92672">=&gt;</span> event <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>),
</span></span></code></pre></div><p>上面的代码将会将流中的数据重复两次输出。</p>
<h4 id="32-streamwhere">3.2 Stream.where</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>stream: controller.stream.where((event) <span style="color:#f92672">=&gt;</span> event <span style="color:#66d9ef">is</span> <span style="color:#66d9ef">int</span>),
</span></span></code></pre></div><p>上面的代码只会允许int类型的数据通过。</p>
<h4 id="33-streamdistinct">3.3 Stream.distinct</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>stream: controller.stream.distinct(),
</span></span></code></pre></div><p>上面的代码将会过滤重复的值。</p>
<h4 id="34-async">3.4 async*</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Stream<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">int</span><span style="color:#f92672">&gt;</span> getNumbers() <span style="color:#66d9ef">async</span><span style="color:#f92672">*</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">while</span>(<span style="color:#66d9ef">true</span>){
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">await</span> Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>));
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">yield</span> <span style="color:#ae81ff">1</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//StreamBuilder使用该流
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>StreamBuilder(
</span></span><span style="display:flex;"><span>	stream: getTime(),
</span></span><span style="display:flex;"><span>  builder: (...) { ... }
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>上面的代码使用<code>async*</code>关键字，每一秒钟会向流中发送一个数字。</p>
<h4 id="35-流中的状态streamtransform">3.5 流中的状态——Stream.transform</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>StreamBuilder{
</span></span><span style="display:flex;"><span>  stream: _controller.stream.transform(TallyTransformer()),
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//....
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//定义自己的TallyTransformer
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">TallyTransformer</span> <span style="color:#66d9ef">implements</span> StreamTransformer {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">int</span> sum <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span>  StreamController _controller <span style="color:#f92672">=</span> StreamController();
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Stream bind(Stream stream){
</span></span><span style="display:flex;"><span>    stream.listen((event) {		<span style="color:#75715e">//我们听源头的这个stream
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      sum <span style="color:#f92672">+=</span> event;
</span></span><span style="display:flex;"><span>      _controller.add(sum);
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> _controller.stream;	<span style="color:#75715e">//他人听的是这个新的stream
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  StreamTransformer<span style="color:#f92672">&lt;</span>RS, RT<span style="color:#f92672">&gt;</span> cast<span style="color:#f92672">&lt;</span>RS, RT<span style="color:#f92672">&gt;</span>() <span style="color:#f92672">=&gt;</span> 
</span></span><span style="display:flex;"><span>    StreamTransformer.castFrom(<span style="color:#66d9ef">this</span>);	<span style="color:#75715e">//类型转换
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
  </channel>
</rss>
