2

I want to get all my email in a list but my promises array is empty. I think it related to some asynchronous problems but I can't figure out what's the problem.

var promises = [];

    const imap = new Imap(imapConfig);
    imap.once("ready", () => {
        imap.openBox("INBOX", false, () => {
            imap.search(["ALL", ["SINCE", new Date()]], (err, results) => {
                const f = imap.fetch(results, { bodies: "" });
                f.on("message", (msg) => {
                    msg.on("body", (stream) => {
                        simpleParser(stream, async (err, parsed) => {
                            const { from, subject, textAsHtml, text } = parsed;
                            promises.push(text);
                        });
                    });
                });
                f.once("end", () => {
                    console.log("Done fetching all messages!", promises);
                    imap.end();
                });
            });
        });
    });

    imap.connect();

    return await Promise.all(promises);
MaxBrt18
  • 63
  • 1
  • 9
  • Uh, there's no promise in your `promises` array? – Bergi Jan 24 '22 at 21:10
  • Yeah when I return this array, it's empty – MaxBrt18 Jan 24 '22 at 21:13
  • Yes, because you fill it in an asynchronous callback. (Multiple nested ones, even). [Make a promise](https://stackoverflow.com/q/22519784/1048572) for [each single asynchronous step](https://stackoverflow.com/a/34308801/1048572), then you can wait for them. – Bergi Jan 24 '22 at 21:15
  • Sorry I'm very new with promises, my problem is to get the result from simpleParser() function. (see below) var test = simpleParser(stream, async (err, parsed) => { const { from, subject, textAsHtml, text } = parsed; return text; } }); promises.push(test); – MaxBrt18 Jan 24 '22 at 21:56
  • Yes, but also getting the result from all the other functions you're calling: `connect`, `openBox`, `search`, `fetch`… `simpleParser` is just the last bit in the chain. Make separate functions for each of them, returning promises that resolve with the result of the respective action (and reject if there's an error!). – Bergi Jan 24 '22 at 21:59

1 Answers1

0

The issue is arising due to mixing callbacks with promises. A callback is not synchronous. The way your code works as of now is:

var promises = [];
const imap = new Imap(imapConfig);
imap.once("ready", () => {/*async code*/});
return await Promise.all(promises);

For the simplest solution, you need to wrap the logic inside a new Promise object.

Using this, the solution would be:

let promise = new Promise((resolve, reject) => {
    var promises = [];
    const imap = new Imap(imapConfig);

    imap.once("ready", () => {
        imap.openBox("INBOX", false, () => {
            imap.search(["ALL", ["SINCE", new Date()]], (err, results) => {
                const f = imap.fetch(results, { bodies: "" });
                f.on("message", (msg) => {
                    msg.on("body", (stream) => {
                        simpleParser(stream, async (err, parsed) => {
                            const { from, subject, textAsHtml, text } = parsed;
                            promises.push(text);
                        });
                    });
                });
                f.once("end", () => {
                    console.log("Done fetching all messages!", promises);
                    imap.end();
                    resolve(promises);
                });
            });
        });
    });
    imap.connect();
});

let promises = await promise;
return await Promise.all(promises);

Other possible solution: promisify callbacks https://zellwk.com/blog/converting-callbacks-to-promises/

stWrong
  • 334
  • 2
  • 14
  • This still doesn't work - it doesn't wait for the `simpleParser` calls. – Bergi Jan 24 '22 at 21:12
  • 1
    @Bergi Thanks for pointing out the anti-pattern. It is easier to wrap the logic as `promise` and wait on it. Edited the answer accordingly – stWrong Jan 24 '22 at 22:09