Promise的顺序执行和并行执行
并行和顺序执行的前提当然是,有一堆promise等着你去执行……
通常我们把这“一堆”promise对象放到一个数组里,[promise1, promise2, promise3, ...]
我们都知道想要让promise按顺序执行,那就是一个接一个的then。然而手写很多then太累了,而嵌套的promise又是反模式,我之前蠢蠢的用递归解决了顺序执行的问题,后来终于在udacity习得了顺序promise的正确打开方式!
场景
假如我们要去拿github按关键字搜索google, amazon, facebook,每个关键字搜出的第一个用户的第一个repo的名字。
单独拿一个,比如google,过程是这样的:
- 请求https://api.github.com/search/users?q=google ,得到用户列表,读取第一个用户的repos_url
- 请求上一步读取的repous_url,获得repo列表,读取第一个repo的名字
显然,第二步是依赖于第一步的执行结果的。
因此,很容易想到下面几种情况:
等第一步全部执行完,再执行第二步(并行执行)
1
2
3
4
5|--------------------|
google第一步 |------------------|
amazon第一步 google第二步
facebook第一步 amazon第二步
facebook第二步对第一步的顺序有要求,必须严格按照google, amazon, facebook的顺序执行(顺序执行)
1
2
3
4
5
6
7|---------|
google1 |---------|
google2 |---------|
amazon1 |---------|
amazon2 |---------|
facebook1 |--------|
facebook2google1执行完就执行google1,amazon1执行完就执行amazon2,但对google, amazon, facebook的顺序没有要求
1
2
3
4
5
6
7
8
9|------------------|
google1 |-------------|
google2
|---------|
amazon1 |-----------|
amazon2
|------------|
facebook1 |-------------|
facebook2
Promise并行执行
对于场景1,主要就是用到Promise.all
,因为是数组,所以在处理的过程中通常会用到.map
或.forEach
可以看到3个user请求是同时进行(并行),3个repos请求也是同时进行(并行),由于使用了Promise.all
,所以repos请求等待users请求全部完成才开始。
promise顺序执行的正确打开方式
对于场景2,有一个小技巧,我第一次看到的时候感受是…惊为天人
按照顺序执行,容易想到的有以下几种方法:
- then.then.then,从头then到尾…
- then(then(then())),then的嵌套…
promise链的本质其实就是从头then到尾,但是第一种方法怎么用程序来实现,也就是上面提到的小技巧,就是值得学习的地方了。(反正我觉得太巧妙了!!!我自己就想不到…)
大概跟在做求和运算时候的思想一样:1
2
3
4
5//求和的时候通常这么做,先定义一个sum,然后依次往里做加法
var sum = 0;
array.forEach(function(item) {
sum = sum + item;
});
1 | //要得到一个then then then的promise链,先定义一个已经resolve了的promise,然后依次往后then… |
下面给出场景2的代码:
可以看到请求是按顺序依次发出
并行执行和顺序执行混用
场景3:
可以看到users请求是并行发出,但完成时间不一样,而repos请求是在对应的users请求完成后就立即执行
混用的另一种情况
有了以上的知识,很容易写出最后一种混用的情况,users按顺序执行,等users全部执行完之后并发执行repos。我比较懒…这个我就不写了…
补充知识:
浏览器的fetch API
本文跟fetch不是充分必要关系,只是我太懒了不想写太多的代码来举例。你可以把它理解为是用Promise包住的$.ajax
,也就是fetch返回一个promise对象。关于fetch的详细用法请参考MDN Fetch_ API
如何查看请求的发送是并行还是顺序
打开chrome,按F12,选中Network选项卡,在No throttling这个下拉列表选GPRS,再运行代码,然后就能清楚的看到各个请求的时间线了。
如果看到的区别不是很明显,很可能是已经缓存了,清空浏览器的缓存在重复上面的步骤就能看到比较明显的区别了。