Как обещать MongoDB/Mongoose .findOne перед .push в массиве .forEach?

Просмотрел примеры bluebird readMe, и мне все еще интересно, как реализовать/преобразовать некоторый асинхронный код для включения промисов с .then..

Здесь есть ряд ifStatements, хотя главное заключается в том, что при циклическом просмотре toArray, если element существует в базе данных (findOne), затем назначьте его переменной, чтобы .push его в поле во встроенном документе новый (.post и .save) БД док.

Вот текущий асинхронный код, который, следовательно, запускает findOne после .save .., но его нужно запустить раньше:

// create a story (accessed at POST http://localhost:4200/api/v1/story)
.post(function(req, res) {

    console.log('posting a new Story..from: ' + res.locals._id + '..' + res.locals.username );

    var story = new Models.Story();

    var toArray = req.body.to;
    console.log(toArray); // [ 'user1', 'user2', 'user3' ]
    toArray.forEach(toArrayLoop);
    function toArrayLoop(element, index, array){

        console.log('element: ' + element); // 'user1' .. 'user2' .. 'user3'

        var out = false; // if sent to Self, out = true
        if (element == res.locals.username) {out = true; console.log('to element: ' + element + ' == res.locals.username: ' + res.locals.username)}

        var toUserId = '';
        if (element) {
            Models.User.findOne({username: element}, function (err, user) {

                if (user) {

                if (err) {
                    console.log(err);
                    res.send(err);
                }
                    console.log('user._id = ' + user._id);
                    toUserId = user._id;
                } else {
                    toUserId = '';
                    console.log('toUserId = ' + toUserId);
                }

            });
        }

        story.to.push({

            user : toUserId, // push the findOne user._id
            username : element, // push the toArray element
            view :
            {
              inbox: true,
              outbox: out,
              archive: false,
            },
            updated : req.body.nowDatetime

        });

    }

    var archive = false;
    console.log('req.body.archive = ' + req.body.archive);
    if (req.body.archive == 'true') { archive = true; console.log('archive = ' + archive); };

    var in = false;
    toArray.forEach(fromSelfLoop);
    function fromSelfLoop(element, index, array){
        console.log('checking if sent to Self: ' + element); // 'user1' .. if matches res.locals: (sent from/to Self)
        if (element == res.locals.username) {in = true; console.log('from element: ' + element + ' == res.locals.username: ' + res.locals.username)}
    } // if sent to Self, archive = true

    story.from.push({

        user : res.locals._id,
        username : res.locals.username,
        view :
        {
          inbox: in,
          outbox: true,
          archive: archive,
        },
        updated : req.body.nowDatetime

    });

    story.title = req.body.title;
    // ..even more doc val assignments..

    console.log('To: ' + req.body.to);
    console.log('Story: ' + req.body.title);

    story.save(function(err, result) {

        if (err) {
            console.log(err);
            res.send(err);
        }
        console.log("The result: ", result);
            res.json({ message: 'Story "' + story.title + '" Created' });

    });

    console.log('post success!');

})

person StackThis    schedule 11.07.2014    source источник


Ответы (1)


На мой взгляд, вы переусердствуете, Promises предоставляют способы без проблем синхронизировать это из коробки.

Вы можете использовать методы агрегации промисов (в данном случае .join и .props) для непосредственного сопоставления со свойствами и получения значений.

Предполагая, что вы обещали Mongoose (так что Bluebird обещает, а не Mongoose).

var story = new Models.Story();
var toArray = req.body.to; // [ 'user1', 'user2', 'user3' ]
var to = Promise.map(toArray,function(element){
    return Promise.props({ // resolves all properties
        user : Models.User.findOneAsync({username: element}), 
        username : element, // push the toArray element
        view : {
            inbox: true,
            outbox: element == res.locals.user.username,
            archive: false
        },
        updated : req.body.nowDatetime
    });
});
var from = Promise.map(toArray,function(element){ // can be a normal map
    return Promise.props({
        user : res.locals._id,
        username : res.locals.username,
        view : {
            inbox: element == res.locals.user.username,
            outbox: true,
            archive: archive,
        },
        updated : req.body.nowDatetime
    });
});
Promise.join(to, from, function(to, from){
    story.to = to;
    story.from = from;
    story.title = req.body.title;
    return story.save(); 
}).then(function(){
    console.log("Success! Story saved!");
}).catch(Promise.OperationalError, function(e){
    // handle error in Mongoose save findOne etc, res.send(...)
}).catch(function(e){
    // handle other exceptions here, this is most likely
    // a 500 error where the top one is a 4XX, but pay close
    // attention to how you handle errors here
});
person Benjamin Gruenbaum    schedule 11.07.2014
comment
Тем не менее, один вопрос: «сообщение» ajax вызывает функцию «успех», которая запускает «get» ajax для API .get, который следует за .post выше.. Однако с реализованным ответом обещания «get» ajax не запускается в консоли браузера. Любые предложения? Любая хорошая обработка улова? - person StackThis; 12.07.2014
comment
Я понятия не имею, где находится ajax, разве это не серверная часть? - person Benjamin Gruenbaum; 12.07.2014
comment
Да, это серверная часть.. Это все входит в .post(function(req, res) {? Кроме того, есть ли какие-либо примеры обработки захвата bluebird... что-то эквивалентное console.log(err);? - person StackThis; 12.07.2014
comment
Это входит в .post, AJAX на стороне клиента. - person Benjamin Gruenbaum; 12.07.2014
comment
Хорошо, спасибо. По какой-то причине ajax не будет читать/получать «успех» этого вызова .post? .. потому что после замены .post в вопросе вашим удивительным обещанием .post ответ функция, вызываемая при успехе ajax 'post', не запускается (однако, когда .get вызывается через ajax на page.load, .get работает, поэтому проблема выглядит многообещающей. - person StackThis; 13.07.2014