Javascript Promise - 第二个然后在第一个之前运行

Posted

技术标签:

【中文标题】Javascript Promise - 第二个然后在第一个之前运行【英文标题】:Javascript Promise - Second then runs before first one 【发布时间】:2022-01-20 16:16:46 【问题描述】:

我创建了一个简单的脚本来获取 Strava 活动并将它们保存到 mongoDB,但我遇到了以下问题:

在获取和提交活动之前,我的数据库连接已断开。以下代码输出:

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)
        )

输出

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

【问题讨论】:

activity.save() 之前的小 return 可能已经成功了。 不,需要更多的重构。 【参考方案1】:

for 循环在其中运行的 then-callback 不会返回承诺,因此链接到它的 then-callback 将在下一个微任务中启动。

for 循环中的承诺应该朝着then-callback 将返回的承诺构建,但activity.save().then(...) 返回的承诺被忽略。

当你使用async await 语法时,事情变得更容易了:

(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);

【讨论】:

【参考方案2】:

只有在所有 promise 实例都结束时才尝试通过 DB 断开连接,如下所示:

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(() => )
    .catch(err => 
        console.log(err)
    ).finally(() => 
        console.log('Start disconnect')
        mongoose.disconnect().then(() => 
            console.log("DB disconnected successfully")
        )
    )

这样他们只有在咨询结束时才会断开连接

【讨论】:

以上是关于Javascript Promise - 第二个然后在第一个之前运行的主要内容,如果未能解决你的问题,请参考以下文章

在 Promise 中,使用 catch 和 then 的第二个参数有啥区别? [复制]

Javascript 像 Java 8 上的“Promise.all”(可能带有 lambdas)

ES6新特性:Javascript中内置的延迟对象Promise

关于promise中reject和catch的问题

JavaScript ES6 promiss的理解。

promise 以及相关面试题