2016-09-08 3 views
1

У меня есть следующий код, который создает массив обещаний для сохранения некоторых чисел, затем он дает обещания (используя библиотеку co) и выводит результаты. Однако я не понимаю, что при распечатке вывода он печатает одну и ту же запись 10 раз.обещания с mongoose и es6 не работают как ожидалось

Вот код:

'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"} 

Обратите внимание, что если я объявляю переменную x внутри Promise, то я получаю ожидаемые результаты (например, 10 различных номера на выходе). Другими словами, если я делаю это изменение (см ниже), он работает, как ожидалось:

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

Мой вопрос, почему код ведет себя таким образом? Обратите внимание, что если я повторяю тот же тип теста , но не, используя mongodb/mongoose и просто печатая некоторые цифры, он работает так, как ожидается, даже с x, объявленным вне Promise. Пример кода ниже:

'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} 

ответ

2

Разница не Мангуст против не-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, это то, что он использует для разрешения, и все они разрешены с одинаковым значением.

По сравнению со своим вторым примером:

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

Помните правило: Всегда объявляйте в узком объеме, вы можете.

+1

Да, я знал, что это было лучше, чтобы объявить переменную х внутри обещание и что делать это позволило бы решить проблему (я уже говорил, что в этом вопросе) , но я просто хотел понять, почему я получаю такое поведение. Во всяком случае, большое объяснение, спасибо! – dcp

2

Причина, по которой это происходит, заключается в том, что x находится за пределами области действия в вашей петле. Когда вы запускаете цикл for, вы не изменяете экземпляр других переменных x, а переназначаете значение оригинала x. То, что происходит в том, что окончательное значение x берет на себя то, где значение 9 Num перед другими значениями, были сохранены в Монго и другие обещания в массиве не решить, прежде чем x установлен на 9.

Если вы хотите иметь правильный выход только plae 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); 
}; 
+0

@ T.J.Crowder пункт сборный. Благодаря! – Mike

+1

@ T.J.Crowder я сделал. – Mike