对 mongoose 和 es6 的承诺没有按预期工作
promises with mongoose and es6 not working as expected
我有以下代码创建一个承诺数组来保存一些数字,然后它产生承诺(使用 co 库)并打印出结果。然而,我不明白的是,当它打印输出时,它打印了 10 次相同的记录。
代码如下:
'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 中声明变量 x
,那么我会得到预期的结果(例如,输出中有 10 个不同的数字)。换句话说,如果我进行此更改(见下文),它会按预期工作:
let p = new Promise((resolve,reject) => {
let x = Num(); // <--- declare x inside the promise
.
.
});
我的问题是,为什么代码会这样?请注意,如果我使用 mongodb/mongoose 重复完全相同类型的测试 not 并仅打印一些数字,即使在 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
是在代码所在的循环外声明的,因此所有迭代都会重用该变量。
请注意,上面标记为 A 和 B 的语句彼此异步发生。到 B
发生时, 所有 的迭代已经完成 A
;因为 B
看到分配给 x
的 last 值,这就是它用来解析的内容,并且它们都用相同的值解析。
与你的第二个例子相比:
let p = new Promise((resolve,reject) => {
setTimeout(() => {
x = new Number(i); // *** A
resolve(x); // *** B
}, 300);
});
请注意,这两者现在是同步发生的; B
每次解析时都使用 x
当时的当前值。
这就是两者行为差异的原因。
从根本上说,x
的声明应该更接近它的使用位置,在 promise 初始化回调中:
//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
的其他变量,而是重新分配原始 x
的值。发生的事情是,最终值 x
出现在 Num
值 9 之前其他值被保存到 Mongo 并且数组中的其他承诺在 [= 之前没有解决11=] 设置为 9.
如果您想获得正确的输出,只需在您的 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);
};
我有以下代码创建一个承诺数组来保存一些数字,然后它产生承诺(使用 co 库)并打印出结果。然而,我不明白的是,当它打印输出时,它打印了 10 次相同的记录。
代码如下:
'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 中声明变量 x
,那么我会得到预期的结果(例如,输出中有 10 个不同的数字)。换句话说,如果我进行此更改(见下文),它会按预期工作:
let p = new Promise((resolve,reject) => {
let x = Num(); // <--- declare x inside the promise
.
.
});
我的问题是,为什么代码会这样?请注意,如果我使用 mongodb/mongoose 重复完全相同类型的测试 not 并仅打印一些数字,即使在 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
是在代码所在的循环外声明的,因此所有迭代都会重用该变量。
请注意,上面标记为 A 和 B 的语句彼此异步发生。到 B
发生时, 所有 的迭代已经完成 A
;因为 B
看到分配给 x
的 last 值,这就是它用来解析的内容,并且它们都用相同的值解析。
与你的第二个例子相比:
let p = new Promise((resolve,reject) => {
setTimeout(() => {
x = new Number(i); // *** A
resolve(x); // *** B
}, 300);
});
请注意,这两者现在是同步发生的; B
每次解析时都使用 x
当时的当前值。
这就是两者行为差异的原因。
从根本上说,x
的声明应该更接近它的使用位置,在 promise 初始化回调中:
//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
的其他变量,而是重新分配原始 x
的值。发生的事情是,最终值 x
出现在 Num
值 9 之前其他值被保存到 Mongo 并且数组中的其他承诺在 [= 之前没有解决11=] 设置为 9.
如果您想获得正确的输出,只需在您的 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);
};