前言
大家都知道 Promise 是 ES6 的一个针对回调地狱的一种解决方案, 我们可以用 promise 去封装异步请求, 用链式写法去处理复杂的业务情景, 但是在一些细节问题上很容易犯一些错误
场景 1: 两个或 N 个异步请求相互依赖嵌套
如: 用户页面需要进行两个请求:[请求获取用户信息],[通过这个用户的 id 去请求用户个人消费记录]
- // 用 settimeout 来模拟异步耗时请求
- function getUser(){
- return new Promise((resolve, reject) => {
- setTimeout(function(){
- resolve({'id':1,'name':'cjt','age':24})
- },1000)
- })
- }
- function getRecord(id){
- return new Promise((resolve, reject) => {
- setTimeout(function(){
- let res = '用户 id:'+id+"的记录为 5 条"
- resolve(res)
- },1000)
- })
- }
OK, 现在两个请求的异步操作我们封装好了, 几种实现方式是:
- // 不好的方式
- getUser().then(user => {
- let id = user.id;
- getRecord(id).then(res => {
- console.log(res)
- })
- })
- // 运行结果 用户 id:1 的记录为 5 条
- // 更好的写法
- getUser().then(user => {
- let id = user.id;
- return getRecord(id)
- }).then(res => {
- console.log(res)
- })
- // 运行结果 用户 id:1 的记录为 5 条
好的, 解决这个问题后, 如果特殊业务情况下, 我想在第二个 then 方法中, 调用第一个 then 的 user, 直接调用会报错 undefined
不过按照不好的那种方式处理是可以取到 user 对象的, 但是逻辑和嵌套多了, 代码又变成了金字塔型, 无限的向右侧移动, 所以这种情况的解决办法是 抛弃陈见, 拥抱金字塔, 但是在第一种写法的基础上, 我们稍作修改
- function needBoth(user,record){
- let result = 'user:'+user+',record:'+record
- return Promise.resolve(result);
- }
- function step1(user){
- let id = user.id;
- return getRecord(id).then(record => {
- return needBoth(user,record); // 需要前两个异步结果的方法
- })
- }
- getUser()
- .then(step1)
- .then(result => {
- console.log(result)
- })
- //user:[object Object],record: 用户 id:1 的记录为 5 条
由于你的 promise 代码开始变得更加复杂, 你可能发现自己开始将越来越多的函数抽离到命名函数中, 我发现这样做, 你的代码会越来越漂亮, 就像这样 http://fex.baidu.com/blog/2015/07/we-have-a-problem-with-promises/ :
- putYourRightFootIn()
- .then(putYourRightFootOut)
- .then(putYourRightFootIn)
- .then(shakeItAllAbout);
这就是 promises 的重点.
场景 2: 等 N 个异步请求都成功后, 再执行之后的操作
遇到这种场景我们可以使用 Promise.all(), 比如下面这个文章
80% 面试者都不及格的 JS 题 https://zhuanlan.zhihu.com/p/25855075?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
很经典的一个 JS 面试题
- for (var i = 0; i <5; i++) {
- setTimeout(function() {
- console.log(new Date, i);
- }, 1000);
- }
- console.log(new Date, i);
- // 结果:
- //Thu Apr 19 2018 14:51:51 GMT+0800 (中国标准时间) 5
- //Thu Apr 19 2018 14:51:53 GMT+0800 (中国标准时间) 5
- //Thu Apr 19 2018 14:51:53 GMT+0800 (中国标准时间) 5
- //Thu Apr 19 2018 14:51:53 GMT+0800 (中国标准时间) 5
- //Thu Apr 19 2018 14:51:53 GMT+0800 (中国标准时间) 5
- //Thu Apr 19 2018 14:51:53 GMT+0800 (中国标准时间) 5
原理很简单, 异步事件进入 task queue, 先执行了 for 循环之后的 console
- for (var i = 0; i < 5; i++) {
- (function(i){
- setTimeout(function() {
- console.log(new Date, i);
- }, 1000);
- }(i))
- }
- console.log(new Date, i);
- // 结果:
- //Thu Apr 19 2018 14:51:52 GMT+0800 (中国标准时间) 5
- //Thu Apr 19 2018 14:51:53 GMT+0800 (中国标准时间) 0
- //Thu Apr 19 2018 14:51:53 GMT+0800 (中国标准时间) 1
- //Thu Apr 19 2018 14:51:53 GMT+0800 (中国标准时间) 2
- //Thu Apr 19 2018 14:51:53 GMT+0800 (中国标准时间) 3
- //Thu Apr 19 2018 14:51:53 GMT+0800 (中国标准时间) 4
- // 注意: 这里用 let 不行, 因为 let 的作用于被限制到 for 循环中了, 外部调用会报错
如果我想让他输出 0,1,2,3,4,5 或者 0->1->2->3->4->5 呢?
注: 0,1,2... 表示同时输出, 0->1->2... 表示每次都间隔 1 秒
- //0->1->2->3->4->5
- let task = []; //promise 对象数组
- for (var i = 0; i <5; i++) {
- (function(j){
- task.push(new Promise((resolve) => {
- setTimeout(function() {
- console.log(new Date, j);
- resolve();
- }, 1000 * j);
- }));
- }(i))
- }
- Promise.all(task).then(results => {
- setTimeout(function(){
- console.log(new Date, i);
- },1000)
- })
结果
- Thu Apr 19 2018 15:14:02 GMT+0800 (中国标准时间) 0
- Thu Apr 19 2018 15:14:03 GMT+0800 (中国标准时间) 1
- Thu Apr 19 2018 15:14:04 GMT+0800 (中国标准时间) 2
- Thu Apr 19 2018 15:14:05 GMT+0800 (中国标准时间) 3
- Thu Apr 19 2018 15:14:06 GMT+0800 (中国标准时间) 4
- Thu Apr 19 2018 15:14:08 GMT+0800 (中国标准时间) 5
- //0,1,2,3,4,5
- let task = []; //promise 对象数组
- for (var i = 0; i <5; i++) {
- (function(j){
- task.push(new Promise((resolve) => {
- setTimeout(function() {
- console.log(new Date, j);
- resolve();
- }, 1000);
- }));
- }(i))
- }
- Promise.all(task).then(results => {
- console.log(new Date, i);
- })
结果
- Thu Apr 19 2018 15:14:02 GMT+0800 (中国标准时间) 0
- Thu Apr 19 2018 15:14:02 GMT+0800 (中国标准时间) 1
- Thu Apr 19 2018 15:14:02 GMT+0800 (中国标准时间) 2
- Thu Apr 19 2018 15:14:02 GMT+0800 (中国标准时间) 3
- Thu Apr 19 2018 15:14:02 GMT+0800 (中国标准时间) 4
- Thu Apr 19 2018 15:14:02 GMT+0800 (中国标准时间) 5
来源: http://www.jianshu.com/p/2886b0b91975