NodeJS: дождитесь завершения каждого foreach с Promises, но никогда не закончите - PullRequest
3 голосов
/ 19 июня 2019

Я работаю с Nodejs.У меня есть forEach, который является асинхронным, так как я должен ждать результата внутри forEach.В результате мне нужно дождаться окончания forEach и продолжить работу с результатом цикла.Я нашел несколько решений для ожидания forEach, одно из них использует Promises.Я сделал, хотя, и эти обещания созданы, однако, код после того, как forEach (и, следовательно, обещания) завершены, фактически никогда не выполняется (console.log не печатается).И функция NodeJS просто заканчивается без каких-либо ошибок.

Вот мой код:

var Client = require('ssh2').Client;

// eslint-disable-next-line no-undef
var csv = require("csvtojson");
// eslint-disable-next-line no-undef
var fs = require("fs");
// eslint-disable-next-line no-undef
const config = require('./config.json');
// eslint-disable-next-line no-undef
const os = require('os');
let headerRow = [];
let sumTxAmount = 0;

const filenameShortened = 'testFile';

let csvLists = [];
let csvFile;

const options = {
    flags: 'r',
    encoding: 'utf8',
    handle: null,
    mode: 0o664,
    autoClose: true
}

var conn = new Client();

async function start() {
    const list = await getCSVList();
    let content = fs.readFileSync('./temp.json', 'utf8');
    content = JSON.parse(content);
    var promises = list.map(function(entry) {
        return new Promise(async function (resolve, reject) {
            if (!content['usedFiles'].includes(entry.filename)) {
                const filename = entry.filename;
                csvFile = await getCsv(filename);
                csvLists.push(csvFile);
                console.log('here');
                resolve();
            } else {
                resolve();
            }
        })
    });
    console.log(promises)
    Promise.all(promises)
        .then(function() {
            console.log(csvLists.length, 'length');
        })
        .catch(console.error);
}

start();

«Здесь» печатается один раз (не 8 раз, поскольку длина массива равна 8), но создано 8 обещаний.Нижняя часть, где я печатаю длину массива, не выполняется.

Может кто-нибудь сказать мне, что я делаю не так?Использую ли я Promises и forEach ложно, так как я должен сделать ожидание внутри forEach?

Примечание: getCSVList () и getCsv () являются функциями для получения Csvs с сервера sftp:

function getCSVList() {
    return new Promise((resolve, reject) => {
            conn.on('ready', function () {
                conn.sftp(function (err, sftp) {
                        if (err) throw err;
                        sftp.readdir(config.development.pathToFile, function (err, list) {
                            if(err) {
                                console.log(err);
                                conn.end();
                                reject(err);
                            } else {
                                console.log('resolved');
                                conn.end();
                                resolve(list);
                            }
                        })
                })
            }).connect({
                host: config.development.host,
                port: config.development.port, // Normal is 22 port
                username: config.development.username,
                password: config.development.password
                // You can use a key file too, read the ssh2 documentation
            });
    })
}

function getCsv(filename) {
    return new Promise((resolve, reject) => {
        conn.on('ready', function () {
        conn.sftp(function (err, sftp) {
            if (err) reject(err);
            let csvFile = sftp.createReadStream(`${config.development.pathToFile}/${filename}`, options);
            // console.log(csvFile);
            conn.end();
            resolve(csvFile);
        })
    }).connect({
        host: config.development.host,
        port: config.development.port, // Normal is 22 port
        username: config.development.username,
        password: config.development.password
        // You can use a key file too, read the ssh2 documentation
    });
});
} 

Вывод в моей консоли из всех журналов консоли:

`➜ node server.js
resolved
[ Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> } ]
here`

Ответы [ 2 ]

1 голос
/ 19 июня 2019

Разбейте вашу проблему на части, подтверждая, что они работают по пути.

Вы, между прочим, неправильно используете поток.

Я сделал рабочий пример с ssh2-sftp-clientтак что вы можете использовать его в качестве отправной точки.


Рабочий пример:

var fs = require('fs'); var _ = require('underscore');
var SFTPClient = require('ssh2-sftp-client');
const CONFIG = {
 "SSH_CONN_OPTS":{"host":"XXXXXXXX","port":22,"username":"XXXXXXXX","password":"XXXXXXXX"},
 "CSV_DIRECTORY":"/var/www/html"
}
//---------------
//.:The order-logic of the script is here
function StartScript(){
 console.log("[i] SSH Connection")
 LoadValidationFile(()=>{
  InitializeSFTP(()=>{ console.log("[+] SSH Connection Established")
   ListRemoteDirectory((list)=>{ console.log(`[i] Total Files @ ${CONFIG.CSV_DIRECTORY} : ${list.length}`)
    //console.log(list) //:now you have a 'list' of file_objects, you can iterate over to check the filename
    var csvFileList = [] //store the names of the files you will request after
    _.each(list,(list_entry)=>{ console.log(list_entry)
     if(!CONFIG.USED_FILES.includes(list_entry.name)){ csvFileList.push(list_entry.name) }
    }) 
    //:now loop over the new final list of files you have just validated for future fetch 
    GenerateFinalOutput(csvFileList)
   })
  })
 })
}
//.:Loads your validation file
function LoadValidationFile(cb){
 fs.readFile(__dirname+'/temp.json','utf8',(err,data)=>{ if(err){throw err}else{
  var content = JSON.parse(data)
  CONFIG.USED_FILES = content.usedFiles
  cb()
 }})
}
//.:Connects to remote server using CONFIG.SSH_CONN_OPTS
function InitializeSFTP(cb){
 global.SFTP = new SFTPClient();
 SFTP.connect(CONFIG.SSH_CONN_OPTS)
 .then(()=>{cb()})
 .catch((err)=>{console.log("[!] InitializeSFTP :",err)})
}
//.:Get a list of files from a remote directory
function ListRemoteDirectory(cb){
 SFTP.list(`${CONFIG.CSV_DIRECTORY}`)
     .then((list)=>{cb(list)})
     .catch((err)=>{console.log("[!] ListRemoteDirectory :",err)})
}
//.:Get target file from remote directory
function GetRemoteFile(filename,cb){
 SFTP.get(`${CONFIG.CSV_DIRECTORY}/${filename}`)
     .then((data)=>{cb(data.toString("utf8"))}) //convert it to a parsable string
     .catch((err)=>{console.log("[!] ListRemoteDirectory :",err)})
}
//-------------------------------------------
var csvLists = []
function GenerateFinalOutput(csv_files,current_index){ if(!current_index){current_index=0}
 if(current_index!=csv_files.length){ //:loop
  var csv_file = csv_files[current_index]
  console.log(`[i] Loop Step #${current_index+1}/${csv_files.length} : ${csv_file}`)
  GetRemoteFile(csv_file,(csv_data)=>{
   if(csv_data){csvLists.push(csv_data)}
   current_index++
   GenerateFinalOutput(csv_files,current_index)
  })
 }else{ //:completed
  console.log("[i] Loop Completed")
  console.log(csvLists)
 }
}
//------------
StartScript()

Удачи!

1 голос
/ 19 июня 2019

Promise.all - это метод, который будет возвращать объект обещания, но вы не ожидаете выполнения метода запуска.

function getCSVList() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve([1, 2, 3, 4]);
    }, 1000);
  });
}

function getCsv(params) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(params);
    }, 1000);
  });
}

async function start() {
  const list = await getCSVList();
  const promises = list.map(item => {
    return new Promise(async function (resolve, reject) {
      const csvFile = await getCsv(item);
      console.log('here');
      resolve(csvFile);
    });
  });

  return Promise.all(promises);
}

start().then(res => {
  console.log(res);
});


Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...