@@ -561,9 +561,143 @@ funcs.reduce(function(pre,current),Q(initialVal){
561
561
回到我们一开始读取文件内容的例子。如果现在让我们把它改写成promise链,是不是很简单呢?
562
562
563
563
``` js
564
+ var Q = require (' q' ),
565
+ fs = require (' fs' );
566
+ function printFileContent (fileName ) {
567
+ return function (){
568
+ var defer = Q .defer ();
569
+ fs .readFile (fileName,' utf8' ,function (err ,data ){
570
+ if (! err && data) {
571
+ console .log (data);
572
+ defer .resolve ();
573
+ }
574
+ })
575
+ return defer .promise ;
576
+ }
577
+ }
578
+ // 手动链接
579
+ printFileContent (' sample01.txt' )()
580
+ .then (printFileContent (' sample02.txt' ))
581
+ .then (printFileContent (' sample03.txt' ))
582
+ .then (printFileContent (' sample04.txt' ));
583
+ ```
584
+
585
+ 很有成就感是不是。然而如果仔细分析,我们会发现为什么要他们顺序执行呢,如果他们能够并行执行不是更好吗? 我们只需要在他们都执行完成之后,得到他们的执行结果就可以了。
586
+
587
+ 我们可以通过Q.all([ promise1,promise2...] )将多个promise组合成一个promise返回。
588
+ 注意:
589
+
590
+ 1 . 当all里面所有的promise都fulfil时,Q.all返回的promise状态变成fulfil
591
+ 2 . 当任意一个promise被reject时,Q.all返回的promise状态立即变成reject
592
+
593
+ 我们来把上面读取文件内容的例子改成并行执行吧
594
+
595
+ ``` js
596
+ var Q = require (' q' ),
597
+ fs = require (' fs' );
598
+ /**
599
+ *读取文件内容
600
+ *@private
601
+ */
602
+ function printFileContent (fileName ) {
603
+ // Todo: 这段代码不够简洁。可以使用Q.denodeify来简化
604
+ var defer = Q .defer ();
605
+ fs .readFile (fileName,' utf8' ,function (err ,data ){
606
+ if (! err && data) {
607
+ console .log (data);
608
+ defer .resolve (fileName + ' success ' );
609
+ }else {
610
+ defer .reject (fileName + ' fail ' );
611
+ }
612
+ })
613
+ return defer .promise ;
614
+ }
615
+
616
+ Q .all ([printFileContent (' sample01.txt' ),printFileContent (' sample02.txt' ),printFileContent (' sample03.txt' ),printFileContent (' sample04.txt' )])
617
+ .then (function (success ){
618
+ console .log (success);
619
+ });
620
+ ```
564
621
622
+ 现在知道Q.all会在任意一个promise进入reject状态后立即进入reject状态。如果我们需要等到所有的promise都发生状态后(有的fulfil, 有的reject),再转换Q.all的状态, 这时我们可以使用Q.allSettled
623
+
624
+ ``` js
625
+ var Q = require (' q' ),
626
+ fs = require (' fs' );
627
+ /**
628
+ *读取文件内容
629
+ *@private
630
+ */
631
+ function printFileContent (fileName ) {
632
+ // Todo: 这段代码不够简洁。可以使用Q.denodeify来简化
633
+ var defer = Q .defer ();
634
+ fs .readFile (fileName,' utf8' ,function (err ,data ){
635
+ if (! err && data) {
636
+ console .log (data);
637
+ defer .resolve (fileName + ' success ' );
638
+ }else {
639
+ defer .reject (fileName + ' fail ' );
640
+ }
641
+ })
642
+ return defer .promise ;
643
+ }
644
+
645
+ Q .allSettled ([printFileContent (' nosuchfile.txt' ),printFileContent (' sample02.txt' ),printFileContent (' sample03.txt' ),printFileContent (' sample04.txt' )])
646
+ .then (function (results ){
647
+ results .forEach (
648
+ function (result ) {
649
+ console .log (result .state );
650
+ }
651
+ );
652
+ });
565
653
```
566
654
655
+ ## 结束promise链
656
+
657
+ 通常,对于一个promise链,有两种结束的方式。第一种方式是返回最后一个promise
658
+
659
+ 如 return foo().then(bar);
660
+
661
+ 第二种方式就是通过done来结束promise链
662
+
663
+ 如 foo().then(bar).done()
664
+
665
+ 为什么需要通过done来结束一个promise链呢? 如果在我们的链中有错误没有被处理,那么在一个正确结束的promise链中,这个没被处理的错误会通过异常抛出。
666
+
667
+ ``` js
668
+ var Q = require (' q' );
669
+ /**
670
+ *@private
671
+ */
672
+ function getPromise (msg ,timeout ,opt ) {
673
+ var defer = Q .defer ();
674
+ setTimeout (function (){
675
+ console .log (msg);
676
+ if (opt)
677
+ defer .reject (msg);
678
+ else
679
+ defer .resolve (msg);
680
+ },timeout);
681
+ return defer .promise ;
682
+ }
683
+ /**
684
+ *没有用done()结束的promise链
685
+ *由于getPromse('2',2000,'opt')返回rejected, getPromise('3',1000)就没有执行
686
+ *然后这个异常并没有任何提醒,是一个潜在的bug
687
+ */
688
+ getPromise (' 1' ,3000 )
689
+ .then (function (){return getPromise (' 2' ,2000 ,' opt' )})
690
+ .then (function (){return getPromise (' 3' ,1000 )});
691
+ /**
692
+ *用done()结束的promise链
693
+ *有异常抛出
694
+ */
695
+ getPromise (' 1' ,3000 )
696
+ .then (function (){return getPromise (' 2' ,2000 ,' opt' )})
697
+ .then (function (){return getPromise (' 3' ,1000 )})
698
+ .done ();
699
+
700
+ ```
567
701
这次我们要介绍的是 async 的 ` mapLimit(arr, limit, iterator, callback) ` 接口。另外,还有个常用的控制并发连接数的接口是 ` queue(worker, concurrency) ` ,大家可以去 https://github.com/caolan/async#queueworker-concurrency 看看说明。
568
702
569
703
这回我就不带大家爬网站了,我们来专注知识点:并发连接数控制。
0 commit comments