很多时候,我们会遇到这种情况:
有多个异步操作,而且需要每个异步操作的返回值。
(比如 ajax获取用户名、ajax获取地址、ajax获取banner图片等等)
(比如 ajax获取用户名、ajax获取地址、ajax获取banner图片等等)
比如异步操作 a, b, c, d 然后是要 在 a 的success 里面写上 b, 然后在 b的success里面写上c吗?? (如下代码)
00a(A => { // 异步操作 a 获得 A 01 b(B => { // 异步操作 b 获得 B 02 c(C => {// 异步操作 c 获得 C 03 d(D => { 04 // 异步操作 d 获得 D 05 console.log(A, B, C, D); 06 }); 07 }); 08 }); 09});
很显然 这样很恼人、而且很不好。
- 缩进太长 自带混乱
debuff
- 三个异步操作所需时间是: a, b, c, d 三件异步操作时间之和 并不是取 a, b, c, d 四件事情里面的最大值
不能不行不可以
# 一个可行的方案 ↵
- 用数组保存需要做什么 (数组元素是函数)
- 处理这个数组、执行里面的元素、并传递一个叫做
commit
的函数给他 - 当异步操作成功的时候
commit(obj)
以这种方式提交值给 commit 完成处理 - commit(obj) 会把 obj 里面的属性合并到 最终期望值 {} 上
以下是具体的实现
# todos ↵
用数组保存异步操作
00var todos = [ 01 function getUser(commit){ 02 setTimeout(() => { 03 commit({ // 这里是异步结束的时候 利用 commit 把值回传 04 name: 'eczn', 05 age: 20 06 }, 233); 07 }); 08 }, 09 function getLoc(commit){ 10 setTimeout(() => { 11 commit({ 12 area: '广东广州GDUT某个地方' 13 }); 14 }, 333); 15 }, 16 function getYourReaction(commit){ 17 setTimeout(() => { 18 commit({ 19 heSay: 'hello coder, nice to code you.' 20 }); 21 }, 334); 22 } 23];
![执行 todos[0]](/images/launcher_todos.png)
执行 todos[0]
# launcher ↵
发射器,这里会并发全部的异步操作,并且定义了
commit
00function launcher(processors, cb){ 01 var o = {}; 02 var count = 0; 03 if (processors.length === 0) cb(o); 04 05 processors.forEach((func, idx) => { 06 func(function commit(asyncVal){ 07 // 把 asyncVal 的所有属性合并到 o 上 08 // ( 利用 Object.keys 获取对象全部属性名 ) 09 Object.keys(asyncVal).forEach(key => { 10 o[key] = asyncVal[key]; 11 }); 12 13 count++; 14 // 如果发射器全部发射完毕则调用回调函数 cb 并把 o 作为参数传递 15 if (count === processors.length) cb(o); 16 }); 17 }); 18}
# 调用 ↵
00launcher(todos, function(ecznSay){ 01 // todos 里面存放的异步操作的值由 commit 回调返回 02 // 全部回调跑完的时候 就会执行当前这段函数 并把期望值返回 03 console.log(ecznSay); 04 05 // 按顺序输出 06 ['name', 'area', 'heSay'].forEach(key => { 07 console.log(`${key}: ${ecznSay[key]}`); 08 }); 09});

执行结果
# 不足的地方 ↵
- 没有考虑失败 这里假设了一定可以得到返回值
- 无序并发,没有考虑异步操作的依赖性 这里假设了异步操作之间互不依赖
17 年 9 月更新, Promise 和 Async / Await 才是异步编程的终极方案,当时没接触到这个,构造的这个异步发射器跟 Promise.all 做的事是类似的。。