0

i am trying to use Promise to make something easy, but it appear to be a real nightmare with promises. I think i am missing something with it.

I would Like to :

  1. Fetch some articles in database
  2. Look into each article found and iterate over article.authors Array.
  3. Fetch each author in dataBase (including author.images) for each article
  4. Send back to client the articles List from step One but updated with article.authors.images

I tryed severals ways using Map / Each / spread / Reduce / _.clone / _cloneDeep

But nothing works as expected

Any help would be appreciate

return Promise.bind({})
        .then(function find_article(){
            return Article.find().sort(req.params.sort).skip(req.params.page).limit(req.params.limit).populateAll()
        }).then(function(articles){
            dataBack = articles
            var articlesPromise =  Promise.map(articles,function(article){
                console.log('------------');
                var AuthorsPromise = Promise.map(article.authors,function(author){
                    return User.findOne(author.id).populateAll().then(function(){
                    })
                })
                return Promise.all(AuthorsPromise).then(function(data){
                    console.log('*******************************');
                    console.log(data);
                    return data
                })

            })
            return Promise.all(articlesPromise).then(function(allArticles){
                    console.log('++++++++++++++++++++++++++++++');

                console.log(allArticles);
            })
        })
        .then(function(WhatIsInThere){
            console.log('somethinAfter');
            console.log(WhatIsInThere);
        })

I got Something like this, but is still doesnt work i am still missing the point of the .all()

Oldwo1f
  • 13
  • 4
  • Which database you are using, it is relevant, because the way you are constructing your queries will kill your server, once your database starts to grow – Yerken May 25 '16 at 12:16
  • As for the question, modularize your task into separate steps. Step 1, fetch the articles using databse query wrapped in promises. Step 2, function which takes array of articles using Promise.all and for each article returns another modularized promise, Promise.all again, which takes author and returns author image upon db lookup. Top level api will call the first module, and chains others – Yerken May 25 '16 at 12:20
  • You don't need to call `Promise.all` any more if you already used `Promise.map`. Notice that `Promise.map(arr, f)` is equivalent to `Promise.all(arr.map(f))` – Bergi May 25 '16 at 13:01

1 Answers1

2

This is not trivial task, you need to chain promises and most probably use function like Promise.all() or jQuery.when()

Your code should look like this

// function that fetch from database and returns promise
function getArticleFromDatabase(articleId) {
    return new Promise();
}

// function that fetch from database and returns promise
function getAuthorFromDatabase(authorId) {
    return new Promise();
}

var articleIds = [1, 2, 3]; // lets have some array with article ids
// then turn it into array of promises
var articlePromises = articleIds.map(function(articleId) {
    var articlePromise = getArticleFromDatabase(articleId);
    // return the promise
    return articlePromise.then(function(articleData) {
        // here we have available complete article data
        var articleAuthors = articleData.authors; // supose it's array of author ids
        // lets turn it into author data promises
        var authorsPromises = articleAuthors.map(function(author) {
            return getAuthorFromDatabase(author.id);
        });

        // return new Promise, so our first promise of article data
        // will return promise of article data with authors data
        return Promise.all(authorsPromises)
            .then(function(fullfilledAuthorsData) {
                // fill in authors data
                articleData.authors = fullfilledAuthorsData;
                // return complete article data with authors data
                return articleData;
            });
    });
});

Promise.all(articlePromises).then(function(fullfilledArticleData) {
    // here you have available complete article data from all articles
    // fullfilledActicledata is array mapped from initial array of ids
    // so fullfilledActicleData[0] has data for articleIds[0],
    // fullfilledActicleData[1] has data for articleIds[1] etc.
    // You can use the fullfilledArticleData freely.
});

Based on your code

// this method obviously returns Promise already
var articlesPromise = Article
    .find()
    .sort(req.params.sort)
    .skip(req.params.page)
    .limit(req.params.limit)
    .populateAll();

// attach callback via .then()
articlesPromise
    .then(function(articles) {   
        // here we have fullfilled articles data already
        var articlesWithAuthorsPromises = articles.map(function(article) {
            var authorsPromises = article.authors.map(function(author) {
                return User.findOne(author.id).populateAll();
            });

            return Promise.all(authorsPromises)
                  .then(function(fullfilledAuthors) {
                      article.authors = fullfilledAuthors;
                      return article;
                   })
        })

        // return new Promise
        return Promise.all(articlesWithAuthorsPromises)
    })
    // attach another callback via .then()
   .then(function(fullData) {
        console.log(fullData);
    })
    // you should also listen for errors
    .catch(errorCallback)
Rudolf Gröhling
  • 4,611
  • 4
  • 27
  • 37
  • Thanks for answer that fast. The thing is : i dont have article's ids until i fetch them from database. – Oldwo1f May 25 '16 at 11:30
  • @Oldwo1f the article ids are not important, they are just ilustrative. The important part there is in chaining promises, so in callback of the first promise you build and return another promise, which has callback, that combine the data from first and second promise and returns another promise of complete data. – Rudolf Gröhling May 25 '16 at 11:58
  • yes, but when i deal with multiple Articles and authors, i cant have it back all together. One more thing, is that i use Waterline ORM and i need the _.clone or toObject my items before overiding properties (see here http://stackoverflow.com/questions/26535727/sails-js-waterline-populate-deep-nested-association) – Oldwo1f May 25 '16 at 12:33
  • And the thing is : I got this working for my fetchOneArticle Method. The Probleme append when i want to fetch multiple articles – Oldwo1f May 25 '16 at 12:38
  • Yes, you probably need to call `toObject()` before treating `articles` like JS Object. BUT, if you use ORM, there shall be some way how fetch articles with authors data already joined. I don't know Waterline, but for most DB this shall be fairly simple. – Rudolf Gröhling May 25 '16 at 12:42