关于 【顺序执行Promise】 的一些理解
前言
近日工作上有一个需求,页面上有一个历史记录的东西,存储了最近使用的10张图片(大概知道下背景就好,不用深究)每一次页面刷新,ready后,要去服务器拉取那几张图片的URL,以便在页面上展示,因为产品要求历史记录的图片要按使用的时间顺序排列展示,我想了一下,决定按顺序发起【拉取图片URL】的请求(不要问我为什么)。在使用过程中碰到了问题,网友的答案我不大满意,所以特此总结一下。
套路
处理这类问题的方法有以下两种:
- Promise链式执行
- 利用async/await
我姑且认为大家的Promise是这么发出去的:
1 /** 2 * 不带参数 3 */ 4 function p1() { 5 // ... 前置处理 6 return new Promise((resolve, reject) => { 7 // ... 业务代码 8 9 }) 10 } 11 12 p1().then(res => { 13 // 后续处理 14 }, err => { 15 // 后续处理 16 })
1 /** 2 * 带参数 3 */ 4 function p1(param) { 5 // ... 前置处理 6 return new Promise((resolve, reject) => { 7 // 业务代码 8 9 }) 10 } 11 12 p1(param).then(res => { 13 // 后续处理 14 }, err => { 15 // 后续处理 16 })
接下来我将分【带参数】和 【不带参数】两种情况,分别说明。
代码
不带参数
链式执行法
1 function p1() { 2 return new Promise((resolve, reject) => { 3 resolve(1); 4 }) 5 } 6 7 function p2() { 8 return new Promise((resolve, reject) => { 9 resolve(2); 10 }) 11 } 12 13 function queue(...t) { 14 // 返回一个resolve状态的Promise对象,方便第一次在then中调用异步任务 15 let _queue = Promise.resolve(); 16 let result = []; 17 t.forEach(p => { 18 _queue = _queue.then(p).then(res => { 19 result.push(res); 20 // 为了在最后一个then中获取到结果,因为then返回的是一个新的Promise实例 21 // 如果在外层有存储结果的对象,这里就不用return 22 return result; 23 }); 24 }); 25 return _queue; 26 } 27 28 queue(p1, p2).then(res => { 29 console.log(res); // [1, 2] 30 })
async/await法
1 function p1() { 2 return new Promise((resolve, reject) => { 3 resolve(1); 4 }).then(() => { 5 return "ok"; 6 }) 7 } 8 9 function p2() { 10 return new Promise((resolve, reject) => { 11 resolve(2); 12 }) 13 } 14 15 async function exec() { 16 // ... 前置处理 17 let result = []; 18 console.log("执行p1") 19 let res1 = await p1(); 20 console.log("执行p2"); 21 let res2 = await p2(); 22 // ... 若干promise 23 result.push(res1, res2); 24 return Promise.resolve(result); 25 } 26 27 exec().then(res => { 28 console.log(res); // ["ok", 2] 29 }); 30 31 /** 32 * 注意:此处的代码并不会等exec()函数执行结束后才执行 33 * 因为在exec()中执行的Promise,一经发出会立即执行,then中的处理函数进入微任务队列,等宏任务执行完后, 34 * 再来执行微任务队列中的所有函数,如此反复。所以此处的代码在第一个Promise发出后,就会执行。 35 * 因为此文分享 [Promise顺序执行],所以不过多谈及这个,但还是提一下吧。36 */ 37 console.log("乱入");
这样也可以:
1 function p1() { 2 return new Promise((resolve, reject) => { 3 resolve(1); 4 }).then(() => { 5 return "ok"; 6 }) 7 } 8 9 function p2() { 10 return new Promise((resolve, reject) => { 11 resolve(2); 12 }) 13 } 14 15 async function exec(...t) { 16 // ... 前置处理 17 let result = []; 18 for (let p of t) { 19 let res = await p(); 20 result.push(res); 21 } 22 return Promise.resolve(result); 23 } 24 25 exec(p1, p2).then(res => { 26 console.log(res); // ["ok", 2] 27 });
带参数
链式执行法
1 function p1(id) { 2 return new Promise((resolve, reject) => { 3 resolve(id); 4 }) 5 } 6 7 function queue(ids) { 8 // ... 前置处理 9 let result = []; 10 let _queue = Promise.resolve(); 11 ids.forEach(function(id) { 12 _queue = _queue.then(() => { 13 // 只有这样,后面那个then才可以获取到p1的执行结果 14 return p1(id); 15 }).then(res => { 16 result.push(res); 17 return result; 18 }) 19 }) 20 return _queue; 21 } 22 23 queue([1, 2, 3]).then(res => { 24 console.log(res); // [1, 2, 3] 25 })
这样也可以:
1 function p1(id) { 2 return new Promise((resolve, reject) => { 3 resolve(id); 4 }) 5 } 6 7 function queue(ids) { 8 // ... 前置处理 9 let result = []; 10 let _queue = Promise.resolve(); 11 ids.forEach(function(id) { 12 _queue = _queue.then(() => p1(id).then(res => { 13 result.push(res); 14 return result; 15 })); 16 }) 17 return _queue; 18 } 19 20 queue([1, 2, 3]).then(res => { 21 console.log(res); // [1, 2, 3] 22 })
async/await法
1 function p1(id) { 2 return new Promise((resolve, reject) => { 3 resolve(id); 4 }) 5 } 6 7 async function exec(...ids) { 8 // ... 前置处理 9 let result = []; 10 for (let id of ids) { 11 let res = await p1(id); 12 result.push(res); 13 } 14 return Promise.resolve(result); 15 } 16 17 exec(1, 2).then(res => { 18 console.log(res); // [1, 2] 19 });