当Promise.all遇到引用类型-所有then结果都一样

项目需求

刚用node.js的时候, 为了给前端返回统一的应答格式, 写了一个类似下面的应答格式化的函数.

后面用到Promise.all的时候遇到了自己挖的坑, 做一下记录.

PS: 这个坑不一定是用Promise.all才会出现, 明白原理即可避免在其它地方被自己挖坑.

上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var Promise = require("bluebird");
var _ = require("lodash");
var promiseOpList = [];
var opList = [1, 2, 3];
var result = { code: 0, msg: 'Successful operation', data: 200 };
var resultSucceed = function ({code, msg, data}) {
Object.assign(result, { code: 0, msg: 'Successful operation', data: '' })
Object.assign(result, { data });
return result;
};
_.forEach(opList, function (op, index, array) {
var promiseOp = new Promise(function (resolve, rejcet) {
// 执行一个简单的加法运算
var addOp = 100 + op;
var resultNew = resultSucceed({data: addOp});
resolve(resultNew);
});
promiseOpList.push(promiseOp);
});
Promise.all(promiseOpList).then(function (promiseResultList) {
// 此处会打印三次Promise执行的结果, 猜测一下这里会是什么结果?
console.info(promiseResultList);
});

原因分析

不想去推导结果的同学可以直接展开下面的runkit, 点击’run’, 再展开最后输出的结果即可知道和验证答案.

▼点击此处展开内容▼

原因是”Object.assign”这个方法做”合并”操作的时候仅仅是对原来的result做操作,

它并不会生成一个新的result, 并且result里的各个键值都属于引用类型.

每次resultFormat函数返回的值给resultNew的时候里面的键值也是引用了原来result的键值.

这样resultNew里的值永远会随着result的改变而改变,

这就导致了前面两个Promise虽然在resolve过程中的计算结果没问题

(单步调试时在”var resultNew = resultFormat({data: addOp});”处断点).

但是最后走到”console.info(promiseResultList);”这个地方的时候却得到的是三个一样的结果.

解决问题

解决这个问题非常简单, 在”Object.assign(result, { data });”之前给result赋值而不是Object.assign.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var Promise = require("bluebird");
var _ = require("lodash");
var promiseOpList = [];
var opList = [1, 2, 3];
var result = { code: 0, msg: 'Successful operation', data: 200 };
var resultSucceed = function ({code, msg, data}) {
result = { code: 0, msg: 'Successful operation', data: '' }
Object.assign(result, { data });
return result;
};
_.forEach(opList, function (op, index, array) {
var promiseOp = new Promise(function (resolve, rejcet) {
// 执行一个简单的加法运算
var addOp = 100 + op;
var resultNew = resultSucceed({data: addOp});
resolve(resultNew);
});
promiseOpList.push(promiseOp);
});
Promise.all(promiseOpList).then(function (promiseResultList) {
console.info(promiseResultList);
});
▼点击此处展开内容▼

显示 Gitment 评论