如何在两个异步操作之间暂停?

问题描述

我需要在“/system/backup/save”和“/file/print”之间暂停。因为否则,在显示“/file”目录的内容之前,备份将不会完成。 现在代码正在执行备份,但它给了我一个尚未备份的文件列表。

const RouterOSAPI = require("node-routeros").RouterOSAPI;
const sleep = require('util').promisify(setTimeout);
var hosts = require('./config.json');

async function backup() {
    return new Promise(function (resolve,reject) {

        for (let elem of hosts) {
            const conn = new RouterOSAPI({
                host: elem.host,user: elem.user,password: elem.password
            })
            conn.connect()
                .then((client) => {
                    return conn.write(["/system/backup/save",]).then((data) => {
                        resolve('COMPLETE - OK');
                    }).catch((err) => {
                        reject('ERROR!');
                    });
                    sleep(5000);
                }).then(() => {
                return conn.write("/file/print").then((data2) => {
                    console.log(data2)
                    resolve('CHECK - OK');
                    conn.close();
                }).catch((err) => {
                    reject('ERROR!');
                });

            }).catch((err) => {
                reject('ERROR CONNECT TO ' + elem.name);
            });
        }
    });
}

backup();

解决方法

通常,使用延迟来等待异步进程的完成是一种反模式,您最终要么等待时间不够长,要么等待不必要的长时间。前者当然是比后者更大的问题,但两者都是问题。如果您有任何方法让另一端报告备份完成,那将是您最好的选择。查看 the documentation,似乎 conn.write 的承诺在操作完成之前不应兑现,但我只是浏览了文档,所以可能并非如此。

除此之外:

  1. 不要显式创建承诺,您的 async 函数会自动创建承诺(但无论如何您可能不想要 async 函数)

  2. 不要将 .then/.catch 处理程序与 async 函数混合使用;使用 await

例如,这是一个并行运行备份等的版本,并通过 allSettled 返回一个给出成功/失败的数组:

const RouterOSAPI = require("node-routeros").RouterOSAPI;
const sleep = require('util').promisify(setTimeout);
var hosts = require('./config.json');

async function backup() {
    // Run the hosts in parallel
    return await Promise.allSettled(hosts.map(async (host) => {
        let conn;
        try {
            const c = new RouterOSAPI({
                host: elem.host,user: elem.user,password: elem.password
            })
            const client = await c.connect();
            conn = c;
            await conn.write(["/system/backup/save",]);
            await sleep(5000); // Only if really unavoidable because the 
                               // backup continues even after the promise
                               // from `write` is fulfilled
            await conn.write("/file/print");
            conn = null;
            c.close();
        } catch (e) {
            if (conn) {
                try {
                    conn.close();
                } catch {} // Don't let errors in close shadow previous errors
            }
            throw e;
        }
    }));
}

backup()
.then(results => {
    // Check for status = "rejected" entries in results and report the errors
});

但请注意,由于该函数仅从 allSettled 返回承诺,因此您可能根本不需要 async 函数:

const RouterOSAPI = require("node-routeros").RouterOSAPI;
const sleep = require('util').promisify(setTimeout);
var hosts = require('./config.json');

function backup() {
    // Run the hosts in parallel
    return Promise.allSettled(hosts.map(async (host) => {
        let conn;
        try {
            const c = new RouterOSAPI({
                host: elem.host,]);
            await sleep(5000); // Only if really unavoidable because the 
                               // backup continues even after the promise
                               // from `write` is fulfilled
            await conn.write("/file/print");
            conn = null;
            c.close();
        } catch (e) {
            if (conn) {
                try {
                    conn.close();
                } catch {} // Don't let errors in close shadow previous errors
            }
            throw e;
        }
    }));
}

backup()
.then(results => {
    // Check for status = "rejected" entries in results and report the errors
});

(关于如果 hosts.map 抛出错误会发生什么情况,这两者之间存在细微差别——可能是因为 hosts 不是数组——但这可能并不重要。前者返回一个被拒绝的承诺,后者抛出一个同步错误。)