Mon cas d'utilisation est le suivant. J'ai une application node.js qui rend les appels API limités par un limiteur
de bibliothèque externe. Je fais ensuite des appels API à un taux de 27 appels / par seconde jusqu'à ce que j'aie fini de boucler sur un tableau entier qui contient un paramètre que je dois passer à l'API. Une fois que j'obtiens la réponse de l'API, je veux enregistrer ces données dans un fichier csv.
Mon idée est d'écrire le fichier csv ligne par ligne une fois que j'obtiens le résultat de l'API. J'utilise csv-writer pour cela.
Selon la documentation
"Vous pouvez continuer à écrire des enregistrements dans le même fichier en appelant plusieurs fois writeRecords (mais vous devez attendre l'accomplissement de la promesse de l'appel précédent writeRecords)."
Promise.resolve ()
.then (() => csvWriter.writeRecords (records1))
.then (() => csvWriter.writeRecords (records2))
J'ai aussi pensé à essayer de faire attendre le code jusqu'à la fin des appels d'API et demander au fichier csv d'être écrit alors, cependant, je ne sais pas comment le faire. Je n'ai trouvé aucun exemple en ligne sur quelque chose de similaire. Tous semblent partir d'un objet / tableau. J'ai également essayé d'ajouter une capture pour toutes les exceptions levées même si cela n'est pas conseillé dans la documentation. Quelque chose du genre.
var RateLimiter = require('limiter').RateLimiter; const createCsvWriter = require('csv-writer').createArrayCsvWriter; var limiter = new RateLimiter(27, 'second', true); let final = [xxxxxxxx, xxxxxxx, ..... ] const csvWriter = createCsvWriter({ header: ['NAME'], path: 'out.csv' }); let outp = [] final.forEach((item, index) => { //limit the API calls according to the limiter defined above limiter.removeTokens(1, () => { ExternalAPIcall({ number: item}, (error, response) => { if (error) { console.error(error) } else { console.log(response.status_message) output.push(response.status_message)] const records = output.map(NAME => [NAME]) csvWriter.writeRecords(records) } }) }) })
Voici mon code:
Promise.resolve() .then(() => csvWriter.writeRecords(outp)) .catch(err => console.log(err))
Je m'attendrais à ce que le fichier soit écrit en ligne par et je pourrais alors utiliser append: true
pour écraser le fichier si nécessaire, comme indiqué dans la documentation. Cependant, cette erreur est générée.
(node: 3540) UnhandledPromiseRejectionWarning: Rejet de promesse non géré. Cette erreur est née soit en jetant à l'intérieur d'une fonction asynchrone sans bloc catch, soit en rejetant une promesse qui n'a pas été gérée avec .catch (). (identifiant de rejet: 86) (node: 3540) UnhandledPromiseRejectionWarning: TypeError: Impossible de lire la propriété 'map' d'undefined à ArrayCsvStringifier.getCsvLine (/Users/xxxx/Desktop/nodexcel/node_modules/csv-writer/dist/lib/csv-stringifiers/abstract.js:19:14) sur Array.from.record (/Users/xxxx/Desktop/nodexcel/node_modules/csv-writer/dist/lib/csv-stringifiers/abstract.js:14:61) à Function.from (natif) à ArrayCsvStringifier.stringifyRecords (/Users/xxxxx/Desktop/nodexcel/node_modules/csv-writer/dist/lib/csv-stringifiers/abstract.js:14:32) à CsvWriter. (/Users/xxxx/Desktop/nodexcel/node_modules/csv-writer/dist/lib/csv-writer.js:24:55) à Generator.next () à /Users/xxxx/Desktop/nodexcel/node_modules/csv-writer/dist/lib/csv-writer.js:7:71 à la nouvelle promesse () à __awaiter (/Users/xxxxx/Desktop/nodexcel/node_modules/csv-writer/dist/lib/csv-writer.js:3:12) à CsvWriter.writeRecords (/Users/xxx/Desktop/nodexcel/node_modules/csv-writer/dist/lib/csv-writer.js:22:16) (nœud: 3540) UnhandledPromiseRejectionWarning: Rejet de promesse non géré. Cette erreur est née soit en jetant à l'intérieur d'une fonction asynchrone sans bloc catch, soit en rejetant une promesse qui n'a pas été gérée avec .catch (). (identifiant de rejet: 88)
Merci pour votre temps.
Cordialement, J.
RÉSOLU: Le problème était que writeRecords attendait> comme Array> et je passais un Array. https://knithubs.com blob / 86a57cae5d68f91d738318aff1de3b6d55d6b718 / README.md # csvwriterwriterecordsrecords , Ce bit a résolu le problème.
const records = llenar.map (NAME => [NAME]) csvWriter.writeRecords (records)
J'ai mis à jour le code ci-dessus
3 Réponses :
Utilisez la référence de code suivante. Ici, j'ai pris les clés de l'objet JSON à des fins d'exemple.
let someObject = yourArray[0] // JSON array let csvHeader = [] for(let key in someObject) { csvHeader.push({ id: key, title: key }); } const csvWriter = createCsvWriter({ path: csvFilePath, header: csvHeader }); return await csvWriter.writeRecords(yourArray);
Salut Rahul, merci pour votre temps. Malheureusement, cela ne fonctionne pas. Cette API fonctionne de manière synchrone et donc une fois que j'utilise wait, elle dit que "await n'est valide que dans la fonction async". Merci beaucoup, cependant
Oui, vous devrez envelopper le code dans la méthode async car await est utilisé
Si vous voulez que votre fichier soit écrit ligne par ligne, vous devez attendre ces promesses, Promise.all
ne vous aidera pas.
Cependant, vous pouvez faire exécuter les promesses de manière séquentielle en utilisant la méthode .reduce
.
Vous pouvez essayer le code ci-dessous:
var RateLimiter = require('limiter').RateLimiter; var createCsvWriter = require('csv-writer').createArrayCsvWriter; var limiter = new RateLimiter(27, 'second', true); const csvWriter = createCsvWriter({ header: ['NAME'], path: './out.csv' }); /** Mocked module - you can delete this */ const nexmo = { numberInsight: { get: (item, cb) => { if (typeof cb !== 'function') { cb = () => {} } let msg = { status: 1, status_message: `${item.number} - have passed` }; cb(null, msg); } } } var final = ['john', 'mike', 'elise', 'valentine'] final = final.reduce((accumulator, item, index) => { return accumulator.then(() => { return new Promise(resolve => { limiter.removeTokens(1, (err, remainingReq) => { if (err) { resolve(Promise.resolve(err.message)); } else { nexmo.numberInsight.get({ level: 'basic', number: item }, (error, response) => { if (error) { resolve(Promise.resolve(error.message)); } else { csvWriter.writeRecords([ [response.status_message] ]).then(() => { resolve(response.status_message) }).catch(writerErr => { resolve(writerErr.message); }) } }) } }) }) }) }, Promise.resolve()); final.then(lastPromiseResponse => { console.log('final', lastPromiseResponse) }).catch(err => { console.log('final err', err); })
Merci pour la suggestion @darklightcode. Il renvoie ceci: UnhandledPromiseRejectionWarning: ReferenceError: csvWriter n'est pas défini. En ce qui concerne votre commentaire sur limiteur. Cela devrait être une fonction, n'est-ce pas? Conformément à la documentation
@JavilondoRamires juste pour vous assurer que vous avez remplacé final.forEach
par le code ci-dessus, n'est-ce pas? Je n'ai changé que cette partie de votre code! Ou vous pouvez faire un violon avec le code que vous avez essayé.
Oui, je l'ai fait, c'est un violon pour référence. Merci
Vous ne l'avez pas fait, j'ai mis à jour ma réponse avec la version complète.
Oh je vois maintenant. Merci beaucoup. J'ai essayé et cela n'a toujours pas fonctionné. identifiant de rejet: 2) ". UnhandledPromiseRejection Avertissement: TypeError: record.map n'est pas une fonction
L'erreur qui vous indique ce qui ne va pas TypeError: record.map n'est pas une fonction
, actuellement record.map
n'existe pas dans le code ci-dessus, vérifiez votre code non publié. Quant à cette ligne outp [index, 0] = response.status_message
, vérifiez mon code mis à jour, je l'ai remplacé, outp [index, 0]
sera toujours évalué comme outp [0]
. Vous voudrez peut-être vérifier la fonction ExternalAPIcall
pour cette record.map
.
J'ai mis à jour le violon . L'API répond bien, lorsque je console.log le résultat, il imprime le résultat très bien. (Imprimer success
provenant de response.status_message
)
J'ai mis à jour mon code avec une démo entièrement fonctionnelle. Voici le lien du bac à sable repl.it/repls/LuxuriousElegantTrialsoftware
Merci beaucoup pour votre aide. J'ai également mis à jour ma question avec le code de travail
Ni l'extrait de code mis à jour dans la publication de question de Javilondo ni la réponse de darklightcode ne semblent attendre l'accomplissement de la promesse writeRecords
renvoie avant d'appeler le prochain appel writeRecords
. Cela peut entraîner une perte de données si le flux sous-jacent ne peut pas écrire dans le fichier assez rapidement.
Nous pouvons écrire quelque chose comme ci-dessous, pour nous assurer que writeRecords
est appelé une fois l'appel précédent terminé. Depuis que j'ai utilisé util.promisify
, Node.js v8 + est requis. (Si vous utilisez une version inférieure de Node, écrivez-la vous-même.)
const ExternalAPIcall = (params, callback) => { setTimeout(() => { callback(null, {status_message: `success: ${params.number}`}); }, 0); }; const final = [1, 2, 3, 4, 5, 6, 7, 8, 9];
J'ai exécuté cet extrait de code en définissant ExternalAPIcall
et final
comme suit:
const {RateLimiter} = require('limiter'); const createCsvWriter = require('csv-writer').createArrayCsvWriter; const {promisify} = require('util'); const limiter = new RateLimiter(27, 'second'); const csvWriter = createCsvWriter({ header: ['NAME'], path: 'out.csv' }); const removeTokens = promisify(limiter.removeTokens.bind(limiter)); const callExternalApi = promisify(ExternalAPIcall); final.reduce(async (promise, item) => { await promise; await removeTokens(1); const {status_message} = await callExternalApi({number: item}); const records = [[status_message]]; await csvWriter.writeRecords(records); }, Promise.resolve()).catch(e => { console.error(e.stack); });