web-dev-qa-db-fra.com

Comment obtenir une ligne de lecture synchrone, ou la "simuler" en utilisant async, dans nodejs?

Je me demande s'il existe un moyen simple d'obtenir une ligne de lecture "synchrone" ou au moins d'obtenir l'apparence d'E/S synchrones dans node.js

J'utilise quelque chose comme ça mais c'est assez gênant

var readline = require('readline');
var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  terminal: false
});

var i = 0;
var s1 = '';
var s2 = '';

rl.on('line', function(line){
    if(i==0) { s1 = line; }
    else if(i==1) { s2 = line; }
    i++;
})

rl.on('close', function() {
    //do something with lines
})'

Au lieu de cela, je préférerais que ce soit aussi simple que quelque chose comme

var s1 = getline(); // or "await getline()?"
var s2 = getline(); // or "await getline()?"

Conditions utiles:

(a) Je préfère ne pas utiliser de modules externes ou de descripteur de fichier/dev/stdio, je soumets du code à un site Web de soumission de code et ceux-ci ne fonctionnent pas là-bas

(b) Peut utiliser async/wait ou des générateurs

(c) Doit être basé sur la ligne

(d) Ne devrait pas nécessiter la lecture de stdin entier en mémoire avant le traitement

12
Colin D

Je pense que c'est ce que vous voulez:

const readline = require('readline');

const rl = readline.createInterface({ input: process.stdin , output: process.stdout });

const getLine = (function () {
    const getLineGen = (async function* () {
        for await (const line of rl) {
            yield line;
        }
    })();
    return async () => ((await getLineGen.next()).value);
})();

const main = async () => {
    let a = Number(await getLine());
    let b = Number(await getLine());
    console.log(a+b);
    process.exit(0);
};

main();

Remarque: cette réponse utilise des fonctionnalités expérimentales et nécessite Node v11.7

2
hamid k

Juste au cas où quelqu'un tomberait sur ici à l'avenir

Node11.7 ajout du support pour cela doc_link utilisant async wait

const readline = require('readline');
//const fileStream = fs.createReadStream('input.txt');

const rl = readline.createInterface({
  input: process.stdin, //or fileStream 
  output: process.stdout
});

for await (const line of rl) {
  console.log(line)
}

n'oubliez pas de l'envelopper dans la fonction asynchrone sinon vous obtiendrez reserver_keyword_error

6
Aishwat Singh

Comme le module readline, il existe un autre module appelé readline-sync, qui prend une entrée synchrone.

Exemple:

const reader = require("readline-sync"); //npm install readline-sync
let username = reader.question("Username: ");
const password = reader.question("Password: ",{ hideEchoBack: true });
if (username == "admin" && password == "foobar") {
    console.log("Welcome!")
}
4
Jaidee

En utilisant des générateurs, votre exemple ressemblerait à ceci:

var readline = require('readline');
var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  terminal: false
});

var i = 0;
var s1 = '';
var s2 = '';

var iter=(function* () {
    s1 = yield;
    i++;
    s2 = yield;
    i++;
    while (true) {
        yield;
        i++;
    }
})(); iter.next();
rl.on('line', line=>iter.next(line))

rl.on('close', function() {
    //do something with lines
})

Donc yield agit ici comme s'il s'agissait d'une getline() bloquante et vous pouvez gérer les lignes de la manière séquentielle habituelle.


UPD :
Et une version asynchrone/attente pourrait ressembler à ceci:

var readline = require('readline');
var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  terminal: false
});

var i = 0;
var s1 = '';
var s2 = '';

var continuation;
var getline = (() => {
    var thenable = {
        then: resolve => {
            continuation = resolve;
        }
    };
    return ()=>thenable;
})();
(async function() {
    s1 = await getline();
    i++;
    s2 = await getline();
    i++;
    while (true) {
        await getline();
        i++;
    }
})();
rl.on('line', line=>continuation(line))

rl.on('close', function() {
    //do something with lines
})

Dans ces deux versions "synchrones", i n'est pas utilisé pour distinguer les lignes et n'est utile que pour compter le nombre total d'entre elles.

3
Des Nerger

Voici un exemple, mais cela nécessite de lire tout stdin avant de donner des résultats, ce qui n'est pas idéal

var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    terminal: false
});


function lineiterator() {
    var currLine = 0;
    var lines = [];
    return new Promise(function(resolve, reject) {

        rl.on('line', function (line){
            lines.Push(line)
        })
        rl.on('close', function () {
            resolve({
                next: function() {
                    return currLine < lines.length ? lines[currLine++]: null;
                }
            });
        })
    })
}

Exemple

lineiterator().then(function(x) {
    console.log(x.next())
    console.log(x.next())
})

$ echo test$\ntest | node test.js
test
test
2
Colin D

Essaye ça. Ce n'est toujours pas une réplication parfaite d'une fonction de lecture de ligne synchrone - par exemple async les fonctions se produisent encore plus tard, donc une partie de votre code appelant peut s'exécuter dans le désordre, et vous ne pouvez pas l'appeler depuis l'intérieur d'une boucle for normale - mais c'est beaucoup plus facile à lire que le code .on ou .question typique.

// standard 'readline' boilerplate
const readline = require('readline');
const readlineInterface = readline.createInterface({
        input: process.stdin,
        output: process.stdout
});

// new function that promises to ask a question and 
// resolve to its answer
function ask(questionText) {
  return new Promise((resolve, reject) => {
    readlineInterface.question(questionText, (input) => resolve(input) );
  });
}

// launch your program since `await` only works inside `async` functions
start()

// use promise-based `ask` function to ask several questions
// in a row and assign each answer to a variable
async function start() {
  console.log()
  let name = await ask("what is your name? ")
  let quest = await ask("what is your quest? ")
  let color = await ask("what is your favorite color? ")
  console.log("Hello " + name + "! " + 
    "Good luck with " + quest + 
    "and here is a " + color + " flower for you.");
  process.exit() 
}

MISE À JOUR: https://www.npmjs.com/package/readline-promise l'implémente (code source ici: https://github.com/bhoriuchi/readline-promise/blob /master/src/index.js#L192 ). Il implémente également plusieurs autres fonctionnalités, mais elles semblent également utiles et pas trop sur-conçues, contrairement à d'autres packages NPM qui prétendent faire la même chose. Malheureusement, je ne peux pas le faire fonctionner à cause de https://github.com/bhoriuchi/readline-promise/issues/5 mais j'aime sa mise en œuvre de la fonction centrale:

function ask(questionText) {
  return new Promise((resolve, reject) => {
    readlineInterface.question(questionText, resolve);
  });
}
2
AlexChaffee

Comme je ne sais pas combien de chaînes vous avez besoin, je les mets toutes dans un tableau

N'hésitez pas à commenter si vous avez besoin d'une réponse plus détaillée ou si ma réponse n'est pas exacte:

var readline = require('readline');
var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    terminal: false
});

var i = 0;
var strings = [];

rl.on('line', function(line) {
    // 2 lines below are in case you want to stop the interface after 10 lines
    // if (i == 9)
    //  rl.close()
    strings[i] = line
    i++
}).on('close', function() {
    console.log(strings)
})
// this is in case you want to stop the program when you type ctrl + C
process.on('SIGINT', function() {
    rl.close()
})
0
Amine