Search code examples
javascriptnode.jsmongodbes6-promise

Javascript Promise - Second then runs before first one


I have created a simple script to fetch Strava activities and save them to a mongoDB, but I ran into the following problem:

My database connections gets disconnected before the activities can be fetched and commited. The following code outputs:

const stravaApi = require('strava-v3');
const mongoose = require('mongoose');
const Activity = require('./models/Activity');

mongoose.connect(process.env.DB_CONNECTION, () => console.log('connected to DB'))

strava = new stravaApi.client(accessToken)
strava.athlete.listActivities({per_page: 2})
        .then(payload => {
            let activities = []
            for (let strava_activity of payload) {
                const activity = new Activity({
                        activityId: strava_activity['id'],
                        name: strava_activity['name'],
                        date: strava_activity['start_date'],
                        type: strava_activity['type'],
                        distance: strava_activity['distance']
                })
                activity.save()
                    .then(data => {
                        console.log(`Activity from date ${strava_activity['start_date']} saved to DB`);
                     })
                    .catch(err => {
                        console.log(err)
                    })
            }
        })
        .then(() => {
            console.log('Start disconnect')
            mongoose.disconnect().then(() => {
                console.log("DB disconnected successfully")
            })
        })
        .catch(err => {
            console.log(err)
        })

Output

connected to DB
DB disconnected successfully
(Error message)
(Error message)

Solution

  • the then-callback in which the for loop runs does not return a promise, and so the then-callback that is chained to it, will kick in in the next microtask.

    The promises inside that for loop should be building towards a promise that the then-callback would return, but instead the promises returned by activity.save().then(...) are ignored.

    Things become easier when you use async await syntax:

    (async function () {
        await mongoose.connect(process.env.DB_CONNECTION);
        console.log('connected to DB');
    
        const strava = new stravaApi.client(accessToken);
        const payload = await strava.athlete.listActivities({per_page: 2});
        for (const {id, name, start_date, type, distance} of payload) {
            const activity = new Activity({
                activityId: id,
                name,
                date: start_date,
                type,
                distance
            });
            await activity.save();
            console.log(`Activity from date ${start_date} saved to DB`);
        }
        console.log('Start disconnect')
        await mongoose.disconnect();
        console.log("DB disconnected successfully");
    })().catch(console.log);