How to convert `Promise` to `Array` in `Aggregation of rawCollection`?

I try to use Aggregation of rawCollection like this

// Method
const data = Branches.rawCollection()
        .aggregate([
          {
            $match: { _id: '001' },
          },
          {
            $project: {
              _id: 1,
              name: 1,
              createdAt: 1,
              createdAtText: {
                $dateToString: { format: '%Y-%m-%d', date: '$createdAt' },
              },
            },
          },
        ])
        .toArray()

   // But can't forEach
   _.forEach(data, val => { ... })
1 Like

Here’s one way:

import { Promise } from 'meteor/promise';

const data = Promise.await(Branches.rawCollection()
        .aggregate([
          {
            $match: { _id: '001' },
          },
          {
            $project: {
              _id: 1,
              name: 1,
              createdAt: 1,
              createdAtText: {
                $dateToString: { format: '%Y-%m-%d', date: '$createdAt' },
              },
            },
          },
        ])
        .toArray());
4 Likes

Excuse me Sir,
What difference Promise.await VS async/await???

Basically, anything which is a Promise can be awaited, as long as it’s in an async function. So, another way of doing it would be:

Meteor.methods({
  async myMethod() {
    const data = await Branches.rawCollection()
      .aggregate([
        {
          $match: { _id: '001' },
        },
        {
          $project: {
            _id: 1,
            name: 1,
            createdAt: 1,
            createdAtText: {
              $dateToString: { format: '%Y-%m-%d', date: '$createdAt' },
            },
          },
        },
      ])
      .toArray();

      //...
  }
});

Thanks again,
Could I use await multi times?

Meteor.methods({
  async myMethod() {
    const data = await Branches.rawCollection()
      .aggregate([
      ])
      .toArray();


    const data2 = await Branches.rawCollection()
      .aggregate([
      ])
      .toArray();

    const data3 = await Branches.rawCollection()
      .aggregate([
      ])
      .toArray();
      //...
  }
});

Yes. As long as they’re inside an async function, they will just work.

1 Like

Thanks for your helping :blush:

1 Like

How would I use aggregate() if I wanted it to have a callback and for it to run asynchronously?

Something like:

Collection.rawCollection().aggregrate({...}, function(error, response) {
   ...
});

Is there a way to do it like this? I’m worried about aggregate() blocking the event loop if I await it. I’d rather it run asynchronously if possible.

awaiting a promise is still asynchronous. When the runtime hits an await, it queues a micro-task at the end of the event loop to check if the result is ready and then continues executing the current set of instructions in the loop.

The microtask checks for the result and if it’s still not ready (either because the db is still working, or network delay) it schedules a new microtask and starts the next event loop, where it can continue serving requests or doing other work while waiting.

Once the db operation returns a value, and the microtask is run, it will continue the function where it was paused by the await.

Even if the promise returning function is synchronous, and returns a result immediately, the async function will be paused till the end of the event loop, where the microtask is scheduled.

2 Likes

Ah… Thanks for the info. How does one actually “block the event loop” then? Just with a heavy CPU-intensive function? I always assumed (incorrectly apparently) it was forcing waits on stuff synchronously.

Yeah, usually it’s when you do a large amount of data processing which blocks other requests from being served.
It’s not exactly blocking since it’s still doing work. At which point it’s better to offload the work into a web worker, worker thread or a child process.

You can also block the event loop with Node’s sync versions of functions, like fs.readFileSync.
Node will block the thread while waiting for the result

1 Like