承诺与mongoose和es6不按预期工作

我有下面的代码创build一个承诺数组来保存一些数字,然后它产生的承诺(使用共库),并打印出结果。 但是,我不明白的是,当它打印输出时,会打印相同的logging10次。

这里是代码:

'use strict' const Promise = require('bluebird'); const co = require('co'); const _ = require('lodash'); const mongoose = require('mongoose'); // plug in the bluebird promise library for mongoose mongoose.Promise = Promise; mongoose.connect('mongodb://localhost:27017/nodejs_testing'); const numSchema = new mongoose.Schema({ num: { type: Number, required: true } }); const Num = mongoose.model('Num', numSchema); let promises = []; let x; // create an array of promises to save some numbers for (let i = 0; i < 10; ++i) { let p = new Promise((resolve,reject) => { x = Num(); x.num = i; x.save((err) => { if (err) { reject(err); } else { resolve(x); } }); }); promises.push(p); }; // yield all the promises, then print out the results co(function * () { let res = yield Promise.all(promises); _.each(res, item => { console.log(JSON.stringify(item)); }); mongoose.disconnect(); }); 

这是输出:

 /tmp/test$ node m {"__v":0,"num":9,"_id":"57d1931037a370055f51977c"} {"__v":0,"num":9,"_id":"57d1931037a370055f51977c"} {"__v":0,"num":9,"_id":"57d1931037a370055f51977c"} {"__v":0,"num":9,"_id":"57d1931037a370055f51977c"} {"__v":0,"num":9,"_id":"57d1931037a370055f51977c"} {"__v":0,"num":9,"_id":"57d1931037a370055f51977c"} {"__v":0,"num":9,"_id":"57d1931037a370055f51977c"} {"__v":0,"num":9,"_id":"57d1931037a370055f51977c"} {"__v":0,"num":9,"_id":"57d1931037a370055f51977c"} {"__v":0,"num":9,"_id":"57d1931037a370055f51977c"} 

请注意,如果我在Promise中声明variablesx ,那么我会得到预期的结果(例如输出中有10个不同的数字)。 换句话说,如果我做了这个改变(见下文),它就能按预期工作:

  let p = new Promise((resolve,reject) => { let x = Num(); // <--- declare x inside the promise . . }); 

我的问题是,为什么代码的行为呢? 请注意,如果我重复完全相同types的testing, 而不是使用mongodb / mongoose,只是打印一些数字,即使在Promise之外声明x ,它也可以正常工作。 下面的示例代码:

 'use strict' const Promise = require('bluebird'); const co = require('co'); const _ = require('lodash'); class Number { constructor(num) { this.num = num; } }; let x; let promises = []; for (let i = 0; i < 10; ++i) { let p = new Promise((resolve,reject) => { setTimeout(() => { x = new Number(i); resolve(x); }, 300); }); promises.push(p); }; co(function * () { let res = yield Promise.all(promises); _.each(res, item => { console.log(JSON.stringify(item)); }); }); 

输出:

 /tmp/test$ node t {"num":0} {"num":1} {"num":2} {"num":3} {"num":4} {"num":5} {"num":6} {"num":7} {"num":8} {"num":9} 

区别不是mongoose和非mongoose。 你的代码做了不同的事情。

在你的第一个例子中,你有(见***评论):

 let p = new Promise((resolve,reject) => { x = Num(); // *** A x.num = i; x.save((err) => { if (err) { reject(err); } else { resolve(x); // *** B } }); }); 

…其中x是在代码所在的循环之外声明的,所以所有迭代都重用该variables。

请注意,上面标记为A和B的语句彼此asynchronous发生。 在B发生的时候, 所有的迭代都已经完成了A ; 因为B看到了分配给x最后一个值,这就是它用来解决的问题,并且它们都用相同的值解决。

与你的第二个例子相比:

 let p = new Promise((resolve,reject) => { setTimeout(() => { x = new Number(i); // *** A resolve(x); // *** B }, 300); }); 

请注意,这两个现在正在同步发生; B每次使用分辨率时使用x的当前值。

这就是两者之间行为差异的原因。

基本上,在promise initcallback中, x应该更接近它的使用位置:

 //let x; // *** Not here // create an array of promises to save some numbers for (let i = 0; i < 10; ++i) { let p = new Promise((resolve,reject) => { let x = Num(); // *** Here x.num = i; x.save((err) => { if (err) { reject(err); } else { resolve(x); } }); }); } 

请记住,规则是:总是在最狭窄的范围内声明。

发生这种情况的原因是因为x在for循环的范围之外。 当你运行for循环时,你不会改变实例化x其他variables,而是重新分配原始x的值。 发生了什么是最后的值x是在其他值已被保存到Mongo之前的Num值9和数组中的其他promise在x被设置为9之前不parsing的地方。

如果你想有正确的输出,只需在for循环中放置x

 // create an array of promises to save some numbers for (let i = 0; i < 10; ++i) { let x; let p = new Promise((resolve,reject) => { x = Num(); x.num = i; x.save((err) => { if (err) { reject(err); } else { resolve(x); } }); }); promises.push(p); };