2

I am using promise-mysql in my application where I need to query the information of a book, and then retrieve some books with the same author as its.

Codes:

return Promise.all([
    pool.query(sql, id),
]).then(data=> {
    var book = data[0][0];
    if (book.author) {
        var authorSql = "select * from authors where name = ? limit 10";
        return pool.query(authorSql, book.author);
    } else {
        return Promise.resolve(book);
    }

}).then(object=> {
    if (object.id) {
        // this is a book instance, return it back
        return object
    } else {
        // this should an array of books
        var booksWithSameAuthor = object;
        // but how to get the origin book here, something like:

        var book = xxxx
        book.recommends_by_author = booksWithSameAuthor
        return book
    }
});

As shown, in the second then function, the object maybe a book object or an array like queried data rows, in the later situation, I need the book object too, but I have no idea how to pass it down.

Any ideas?

hguser
  • 35,079
  • 54
  • 159
  • 293
  • Be careful with the word `object`, it is a keyword in javascript (and many other languages) which is why it's being highlighted in blue on SO. I'd recommend using another variation like `obj`. – Soviut Sep 11 '16 at 06:59
  • `object` is a keyword? I don't think so. –  Sep 11 '16 at 07:17
  • `return pool.query(authorSql, book.author).then(authors=>({authors, book}));` - then `booksWithSameAuther = object.authors, book = object.book;` – Jaromanda X Sep 11 '16 at 07:27
  • 1
    @Soviut `object` is not a keyword (and not even `Object` is), the reason to change it (and not to `obj`) is because it's *meaningless*. – Bergi Sep 11 '16 at 11:03
  • 1
    A range of generic solutions to this issue is provided in [How-do I access previous promise results in a then chain](http://stackoverflow.com/questions/28250680/) – Roamer-1888 Sep 11 '16 at 11:10
  • Not really, the later promise is depend on the former. – hguser Sep 11 '16 at 13:58

3 Answers3

2

that whole code snippet can be changed to

return Promise.all([
    pool.query(sql, id),
])
.then(data=> {
    var book = data[0][0];
    if (book.author) {
        var authorSql = "select * from authors where name = ? limit 10";
        return pool.query(authorSql, book.author)
        .then(object=>{
            book.recommends_by_author = object;
            return book;
        });
    } else {
        return book;
    }
});

You use a nested .then (yeah, I know, you want to avoid the pyramid) - in the nested .then you set the recommends_by_author property on book

Note, within the .then, the return Promise.resolve(book) is not required, as .then returns a Promise "by default" - so, return book is enough, both in the "main" .then and in the inner .then

Jaromanda X
  • 53,868
  • 5
  • 73
  • 87
  • You don't call nested `if` blocks a pyramid either, do you? :-D This is the correct solution, +1. – Bergi Sep 11 '16 at 11:04
  • if you need to indent code, you're not doing it right :p - I may be using the wrong term, you know, callback within callback to the nth level, which is what promises are supposed to "fix" :p – Jaromanda X Sep 11 '16 at 11:05
  • to be honest, I wouldn't have the `else` around `return book` - but that's just me – Jaromanda X Sep 11 '16 at 11:08
  • I certainly indent the code in my `if` blocks for readability :-) Promises can only flatten linear chains, not branched ones - you need to flatten each branch separately. Only `async`/`await` will "fix" that problem. – Bergi Sep 11 '16 at 11:09
  • @Bergi - I've yet to get my head fully around async/await - I can see it's appeal, but haven't (personally) found a real world use - besides, professionally, I still have to contend with supporting IE11 - not sure if there's an EASY way to "emulate" async/await in that dinosaur – Jaromanda X Sep 11 '16 at 11:12
  • All major transpilers seem to be able to convert it into a ES5 state machine, it's more trivial than one might think. – Bergi Sep 11 '16 at 11:16
  • thanks @Bergi - I'll give babel a go at it once I fully grok the concept – Jaromanda X Sep 11 '16 at 11:17
1

If you need to pass multiple values down through the promise chain, you can pass an object, rather than a single value.

return {
    book: book,
    somethingElse: obj
};

In your next .then() function, you can retrieve it:

.then(data => {
    console.log(data.book);
    console.log(data.somethingElse);
})
Soviut
  • 88,194
  • 49
  • 192
  • 260
0

You can chain the second pool.query with a .then and alter the book object.

...
  return pool.query(authorSql, book.author).then(authors => {
    book.authorList = authors;
    return Promise.resolve(book);
  });
...

Then you can check if book.authorList is set in your final .then().

theduke
  • 3,027
  • 4
  • 29
  • 28
  • How is `return Promise.resolve(book)` here different from just `return book`? –  Sep 11 '16 at 07:18
  • @torazburo The difference is that the returned promise only finishes after the second query has completed, and the authorList was added to the book object. – theduke Sep 11 '16 at 07:23
  • you only need to `return book`, not `Promise.resolve(book)` in that snippet of code – Jaromanda X Sep 11 '16 at 07:29
  • No. Semantically, they are precisely identical. I understand you just took this from the OP's code, but still it would have been nice to fix it. –  Sep 11 '16 at 07:30