2016-03-10 25 views
9

Так у меня есть коллекция сообщенийКак использовать обещание избежать обратного вызова ада?

{ 
    id: String, 
    comments: [String], # id of Comments 
    links: [String], #id of Links 
} 

Комментарии: { ID: String, комментарий: String, }

Ссылки: { ID: String, ссылка: String, }

Найти сообщение с комментариями и ссылками на него по id:

Posts.findOne({id: id}, function(post) { 
    Comments.find({id: post.id}, function(comments) { 
    Links.find({id: post.id}, function(links) { 
     res.json({post: post, comments: comment, links: links}) 
    }) 
    }) 
}) 

Как использовать Promise (http://mongoosejs.com/docs/promises.html), чтобы избежать обратного вызова ада?

var query = Posts.findOne({id: id}); 
var promise = query.exec(); 

promise.then(function (post) { 
    var query1 = Comments.find({id: post.id}); 
    var promise1 = query1.exec(); 
    promise1.then(function(comments) { 
    var query2 = Links.find({id: post.id}); 
    var promise2 = query2.exec(); 
    promise2.then(function(links) { 
     res.json({post: post, comments: comment, links: links}) 
    }) 
    }) 
}); 

кажется не хороший ......

ответ

4

Try йота это:

function getPost(id) { 
    return Post 
    .findOne({id: id}) 
    .then(post => { 
     return post; 
    }); 
} 

используя Q модуль

function getCommentsAndLinks(post) { 
    return Q.all([ 
    Comment.find({id: post.id}), 
    Links.find({id: post.id}) 
    ]) 
    .done(results => { 
    let comments = results[0]; 
    let links = results[1]; 
    return [post, comments, links]; 
    }) 
    .catch(err => { 
    // handle err 
    }) 

на контроллере

getPost(postId) 
.then(getCommentsAndLinks) 
.then(results => { 
    let post = results[0]; 
    let comments = results[1]; 
    let links = results[2]; 
    // more code here 
}) 
.catch(err => { 
// handle err 
}) 

, но я предлагаю вам не сохранить строку IDS, сохранить экземпляр объекта, так что вы можете использовать Заполнить, чтобы получить все данные комментарии и ссылки, что-то вроде этого:

Post 
.findOne({id: id}) 
.populate('comments') 
.populate('links') 
.then(post => { 
    // here have the post with data of comments and links 
}); 
+0

@Sato - первая версия здесь сериализует 'getComments()' и 'getLinks()', что необязательно. Выполнение их параллельно (как в моем ответе), скорее всего, будет работать лучше. Кроме того, убедитесь, что вы понимаете, как выполнять обработку ошибок либо в ваших обещаниях, либо в обещании. – jfriend00

+0

@ jfriend00 Вы правы, я отредактировал свой ответ, используя модуль [Q] (https://www.npmjs.com/package/q), также вы можете использовать 'Promise.all()', если хотите. – DJeanCar

1

Вы можете сделать это с помощью обещаний, как это:

Posts.findOne({id: id}).exec().then(function(post) { 
    let p1 = Comments.find({id: post.id}).exec(); 
    let p2 = Links.find({id: post.id}).exec(); 
    return Promise.all([p1, p2]).then(function(results) { 
     res.json({post: post, comments: results[0], links: results[1]}); 
    }); 
}).catch(function(err) { 
    // error here 
}); 

Это создает две операции Comments.find().exec() и Links.find().exec() что оба зависят от post но они независимы друг от друга, поэтому они могут работать параллельно. Затем он использует Promise.all(), чтобы узнать, когда оба выполнены, а затем можно вывести JSON.

Вот пошаговое описание:

  1. Run Posts.findOne().exec().
  2. Когда это будет сделано, запустите оба Comments.find().exec() и Links.find().exec() в параллель.
  3. Используйте Promise.all(), чтобы узнать, когда оба из них выполнены.
  4. Когда оба из них выполнены, выведите JSON.

Это может быть сделано с меньшим вложенности, а потому, что вы используете предыдущие результаты в последующих запросах или в окончательном формате JSON, это легче гнездо это немного.

Вы можете увидеть различные варианты совместного использования предыдущих результатов, а также привязать обещания к обещанию в этом другом ответе How to chain and share prior results.


FYI, где реализация этого обещания действительно сияет по сравнению с тем, что вы показываете в своем вопросе, предназначена для обработки ошибок. В коде без обещаний нет обработки ошибок, но обещая версия будет распространять все ошибки до обработчика .catch().

1

Преимущество использования обещаний вы можете приковать их, так что ваш код может быть уменьшен до:

let post, comments; 
Posts.findOne({id: id}).exec().then(_post => { 
    post = _post; 
    return Comments.find({id: post.id}).exec(); 
    }).then(_comments => { 
    comments = _comments; 
    return Links.find({id: post.id}).exec(); 
    }).then(links => res.json({post, comment, links})) 
    .catch(error => res.error(error.message)); 

вы заметите, что мне нужен только один блок поймать.

+1

Лучше избегать [этого шаблона] (http://stackoverflow.com/a/28250700/1048572). – Bergi

2

Вы вложили обратные вызовы. Вам не нужно это делать.Если вы возвращаете обещание .then то любой .then вы приковать к ней будет решен, когда что обещания получает решение:

promise.then(post => Comments.find({id: post.id}) 
    .then(comments => Links.find({id: post.id}) 
    .then(links => {}); 

для комментариев запрос не зависит от ссылок, так что вы на самом деле можете сделать оба запроса сразу:

promise.then(post => { 
    return Promise.all([ 
    post, 
    Comments.find({id: post.id}), 
    Links.find({id: post.id}), 
    ]); 
}).then(data => res.json({ 
    post: data[0], 
    comments: data[1], 
    links: data[2], 
}); 

Если вы используете библиотеку как bluebird вы также можете использовать что-то вроде оператора spread, чтобы сделать имена более прозрачными.


Я также хотел бы посмотреть в использовании co для генератора на основе потока управления, как я думаю, что это еще яснее:

co(function*() { 
    const post = yield Posts.findOne({id}); 
    const [comments, links] = yield [ 
    Comments.find({id: post.id}), 
    Links.find({id: post.id}), 
    ]; 

    res.json({post, comments, links}); 
}); 
+0

Умный способ получить переменную 'post' до следующего обработчика' .then() ', передав ее в' Promise.all() '. – jfriend00

0

Вот несколько короче версия

Posts.findOne({id: id}).then(function (post) { 
    var query1 = Comments.find({id: post.id}); 
    var query2 = Links.find({id: post.id}); 

    Promise.all(query1.exec(), query2.exec()).then(function(data) { 
    res.json({ post: post, comments: data[0], links: data[1] }); 
    }); 
}); 
+0

Это очень похоже на мой ответ. – jfriend00

+0

Да, извините, когда я начал отвечать на ваш ответ, еще не был отправлен. Я видел это только после того, как я разместил свой. – gabesoft

-2

На мой взгляд, вы не можете избежать обратного вызова ад. Это характер асинхронного программирования. Вы должны использовать асинхронное программирование, не пытаясь заставить его выглядеть синхронно.

Вы должны использовать обратный вызов для создания обещания, просто для достижения синтаксиса «then». Синтаксис «then» выглядит лучше, но на самом деле не обеспечивает ничего полезного, кроме обратного вызова, зачем беспокоиться. Единственная полезная функция обещания - Promise.all, которую вы можете использовать, чтобы дождаться завершения всех ваших обещаний.

Попробуйте использовать rxjs для работы с проблемами async. Вам все равно придется использовать обратный вызов для создания наблюдаемого rxjs. Но rxjs предоставляет множество функций, которые помогут вам использовать асинхронное программирование, а не избегать его.

+0

ответы выше показывают, что можно и желательно избегать обратного вызова ада с обещаниями. см. ответ от @Explosion Pills – pungggi

Смежные вопросы