簡體   English   中英

貓鼬和es6的承諾未按預期工作

[英]promises with mongoose and es6 not working as expected

我有以下代碼,該代碼創建一個promise數組以保存一些數字,然后產生promise(使用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重復完全相同的測試類型,而只是打印一些數字,即使使用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}

區別不是貓鼬和非貓鼬。 您的代碼正在做不同的事情。

在第一個示例中,您有(請參閱***注釋):

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最后一個值,所以它使用它來解析,並且都使用相同的值解析。

與第二個示例相比:

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

注意,兩者現在正在彼此同步發生; B每次執行分辨率時都使用x的當時值。

這就是兩者之間行為差異的原因。

從根本上說, 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其他實例,而是重新分配原始x的值。 發生的事情是,最終值x出現在將其他值保存到Mongo之前, Num值9所在的位置,而數組中的其他promise在x設置為9之前沒有解析。

如果要獲得正確的輸出,只需將x放在for循環內:

// 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);
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM