nha
nha

Reputation: 18005

Node.js return result of multiple asynchronous yield synchronously

I am using rethinkDB with node.js. The following request works fine :

function myFn () {
 return co(function *() {
        let query;

        query = yield r.db("my-db")
            .table("app")
            .filter(r.row("id").eq(id))
            .run(conn);

        return query.toArray();
    });
}

I would like to return the result of multiple yield asynchronously, however the following fails :

function myFn () {
 return co(function *() {
        let query, query2;

        query = r.db("my-db")
            .table("app")
            .filter(r.row("id").eq(id))
            .run(conn);


        query2 = r.db("my-db")
            .table("app")
            .filter(...)
            .run(conn);

        return yield {q1 : query, q2 : query2};
    });
}

Then I have to call toArray() on each element, so on the calling function I do :

 // using ramda.js
  var res = R.map((el) => {
            return el.toArray();
        }, yield myFn);

However I get :

{
  "q1": {
    "isFulfilled": false,
    "isRejected": false
  },
  "q2": {
    "isFulfilled": false,
    "isRejected": false
  }
}

Something odd too :

 // this works perfectly
 return q.toArray();

 // this returns the following :
 return {q: q.toArray()};
 "q": {
   "isFulfilled": true,
   "isRejected": false,
   "fulfillmentValue": [  ...  ]
  }

I suspect I am missing something about the way yield works, so how can I return the fulfilled result of multiple yield results ?

Upvotes: 2

Views: 2065

Answers (2)

nha
nha

Reputation: 18005

So I asked the same question on the reThinkDB google groups :

To quote Ryan Paul :

Your original attempt was actually really close, there were just two things that you need to add:

  • You need to yield the queries individually
  • And you need to convert to array in the query if you don’t want to get cursors

Here’s a working example with the coroutine and yield:

function myFn() {
  return co(function*() {
    var conn = yield r.connect();
    var query1 = yield r.db("test").table("fellowship")
                        .filter({species: "hobbit"})
                        .coerceTo("array").run(conn);

    var query2 = yield r.db("test").table("fellowship")
                        .filter({species: "human"})
                        .coerceTo("array").run(conn);

    conn.close();

    return {q1: query1, q2: query2};
  });
}

It’s also pretty simple to do without the coroutines and yield, and as long as you use a promise library like bluebird. Here’s how I would do it:

function myFn1() {
  return r.connect().then(conn => {
    return bluebird.all([
      r.db("test").table("fellowship")
       .filter({species: "hobbit"})
       .coerceTo("array").run(conn),
      r.db("test").table("fellowship")
       .filter({species: "human"})
       .coerceTo("array").run(conn)
    ]).then(items => ({q1: items[0], q2: items[1]}));
  });
}

Upvotes: 1

Bergi
Bergi

Reputation: 664620

yield doesn't work with objects that contain promises - it only works with promises itself. Instead of return yield {q1: query, q2: query2}; you'd have to do

return {q1: yield query, q2: yield query2};

However, that is kinda problematic as errors in query2 will not be thrown until query is done. So if you don't just want to sequentially execute them, you will have to use Promise.all to await a collection of promises "in parallel":

var [q1, q2] = yield Promise.all([query, query2]);
return {q1, q2};

(Depending on the promise lib you're using, there might also be a helper function to treat objects as collections, not only arrays)

Upvotes: 1

Related Questions