首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对猫鼬和es6的承诺不像预期的那样工作

对猫鼬和es6的承诺不像预期的那样工作
EN

Stack Overflow用户
提问于 2016-09-08 16:47:49
回答 2查看 295关注 0票数 1

我有下面的代码,它创建一系列承诺来保存一些数字,然后生成承诺(使用co库)并输出结果。但是,我不明白的是,当它打印输出时,它会打印10次相同的记录。

以下是代码:

代码语言:javascript
复制
'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();
});

这是输出:

代码语言:javascript
复制
/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"}

注意,如果我在承诺中声明变量x,那么我将得到预期的结果(例如,输出中的10个不同的数字)。换句话说,如果我做了这一更改(见下文),它将如预期的那样工作:

代码语言:javascript
复制
  let p = new Promise((resolve,reject) => {
    let x = Num(); // <--- declare x inside the promise
    .
    .
  });

我的问题是,为什么代码是这样的呢?请注意,如果我重复完全相同类型的测试--不使用mongodb/mongoose而只打印一些数字--即使在承诺之外声明的x也能正常工作。示例代码如下:

代码语言:javascript
复制
'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));
  });
});

输出:

代码语言:javascript
复制
/tmp/test$ node t
{"num":0}
{"num":1}
{"num":2}
{"num":3}
{"num":4}
{"num":5}
{"num":6}
{"num":7}
{"num":8}
{"num":9}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-09-08 16:56:05

区别不是猫鼬和非猫鼬。您的代码正在执行不同的操作。

在第一个示例中,您有(请参阅***注释):

代码语言:javascript
复制
let p = new Promise((resolve,reject) => {
  x = Num();               // *** A
  x.num = i;
  x.save((err) => {
    if (err) {
      reject(err);
    } else {
      resolve(x);          // *** B
    }
   });
});

...where x是在代码所在的循环之外声明的,因此所有迭代都重用变量。

请注意,上面标记为A和B的语句彼此异步发生。当B发生时,迭代的所有都已经完成了A;自从B看到分配给x最后一个值以来,这就是它用来解析的,并且它们都是用相同的值解析的。

与第二个示例相比:

代码语言:javascript
复制
let p = new Promise((resolve,reject) => {
  setTimeout(() => {
    x = new Number(i);     // *** A
    resolve(x);            // *** B
  }, 300);
});

请注意,这两种情况现在是同步发生的;B每次执行解析时都使用当时的x值。

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

从根本上说,应该在init回调承诺中,将x声明得更接近其使用的位置:

代码语言:javascript
复制
//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);
      }
     });
  });
}

记住,规则是:始终在尽可能窄的范围内声明。

票数 2
EN

Stack Overflow用户

发布于 2016-09-08 16:57:59

之所以会发生这种情况,是因为x超出了for循环中的作用域。当您运行for循环时,您不会更改实例化x的其他变量,而是重新分配原始x的值。正在发生的情况是,最终的x值是在将其他值保存到Mongo之前的Num值9,而数组中的其他承诺在x设置为9之前不解析。

如果您想要有适当的输出,只需在for循环中配置x

代码语言:javascript
复制
// 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);
};
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39396375

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档