как превратить синтаксис "Promise" Child_process.spawn в синтаксис "async / await" - PullRequest
1 голос
/ 26 октября 2019

Итак, у меня есть этот код, и я пытаюсь полностью понять синтаксис async / await. Ниже приведена версия кода Promise :

function callToolsPromise(req) {
    return new Promise((resolve, reject) => {
        let pipshell = 'pipenv';
        let args = ['run', 'tools'];
        req.forEach(arg => {
            args.push(arg)
        });
        tool = spawn(pipshell, args);
        tool.on('exit', (code) => {
            if (code !== 0) {
                tool.stderr.on('data', (data) => {
                    reject(data);
                });
            } else {
                tool.stdout.on ('data', (data) => {
                    resolve(JSON.parse(data)):
                });
            }
        });
    })
}

У меня есть некоторый код Python, который я хочу выполнить в tools / __ main__.py, поэтому я и вызываю "pipenv".

Вот моя попытка написать это async / await (что на самом деле работает):

async function callToolsAsync(req) {
    let pipshell = 'pipenv';
    let args = ['run', 'tools'];
    req.forEach(arg => {
        args.push(arg)
    });
    let tool = spawn(pipshell, args);
    for await (const data of tool.stdout) {
        return data
    }
}

Но все, что я сделал, это скопировал и вставил из чьего-либопример, где у меня есть цикл for await....

Поэтому я пытался переписать этот код, чтобы я мог его понять, но у меня не получалось уже несколько дней.

Есть ли другие способы написания этого кода с помощью async / await без использования цикла for await...?

Также я понятия не имею, как получить доступ к данным, кроме использования .thenсинтаксис:

callToolsAsync(['GET','mailuser'])
.then(console.log)

Как еще я могу получить доступ к "данным" из resolve(data)?

Большое спасибо.

Ответы [ 2 ]

1 голос
/ 26 октября 2019

Вероятно, нет лучших способов написания кода с использованием async/await, чем использование синтаксиса for async (chunk of stream) в Node. В потоках узлов реализован асинхронный итератор, специально предназначенный для этого.

В этой статье о 2ality содержится более подробное объяснение и обсуждение. Статьи MDN по Symbol.asyncIterator и for-await ... из более широко охватывают асинхронную итерацию.

После того, как было решено использование асинхронной итерации, ее необходимо использовать вasync функция, которая будет возвращать обещание.

Хотя использование выражения then в возвращенном обещании является совершенно нормальным способом получения данных, вы также можете await получить результат callToolsAsync(req) - при условии, конечно, что вызов закодирован внутри функции async, так что await находится в допустимом контексте.


Следующий код эксперимент получает * 1028вывод * и stderr и код завершения дочернего процесса. Он не использует Python и не анализирует данные.

main.js (введите node main.js для запуска)

// main.js
async function spawnChild() {
    const { spawn } = require('child_process');
    const child = spawn('node', ["child.js"]);

    let data = "";
    for await (const chunk of child.stdout) {
        console.log('stdout chunk: '+chunk);
        data += chunk;
    }
    let error = "";
    for await (const chunk of child.stderr) {
        console.error('stderr chunk: '+chunk);
        error += chunk;
    }
    const exitCode = await new Promise( (resolve, reject) => {
        child.on('close', resolve);
    });

    if( exitCode) {
        throw new Error( `subprocess error exit ${exitCode}, ${error}`);
    }
    return data;
}

spawnChild().then(
    data=> {console.log("async result:\n" + data);},
    err=>  {console.error("async error:\n" + err);}
);

child.js

// child.js
console.log( "child.js started"); //  stdout
setTimeout( finish, 1000);
function finish() {
    console.log( "child.js: finish() call");  //  stdout 
    console.error("child exit using code 1"); //  stderr
    process.exit(1);
}

Это показало

  • Консольное предупреждение о том, что асинхронная итерация читаемого потока все еще является экспериментальной в узле,
  • Кажется, что циклы for await (chunk of stream)цикл до тех пор, пока поток не будет закрыт - в этом случае это означает, что await будет ожидать открытого потока, у которого нет данных, доступных в то время.
  • извлекает содержимое stdout и stderr из своих каналови получение кода завершения может быть выполнено в произвольном порядке, поскольку поиск выполняется асинхронно.
  • Объединение фрагментов данных, поступающих через каналы из другого процесса, не является обязательным - консольные журналы от дочернего процессапришел через отдельно.
1 голос
/ 26 октября 2019

Важно понимать, что async/await и обещания одинаковы , только разный синтаксис.

Так что каждая асинхронная функция возвращает обещание !

при условии, что у вас есть функция, повторяющая обещание:

function foo() {
  return new Promise(resolve => setTimeout(() => resolve("done"), 1000));
}

есть два способа использования значения.

Стиль обещания:

function test() {
  foo().then(value => console.log(value));
}

илиasync await:

async function test() {
  const value = await foo();
  console.log(value);
}

Теперь важно понимать, что ваша оригинальная callToolsPromise функция не в стиле обещания. при работе с обещаниями вы никогда не звоните new Promise. По сути, вся идея new Promise состоит в том, чтобы преобразовать асинхронный не обещающий код (и, следовательно, не асинхронный / ожидающий, как тот же самый) в Обещания (и так же асинхронный / ожидающий).

Сейчас асинхронный означает не async/await, а более общую концепцию. Другим распространенным способом обработки асинхронности являются обратные вызовы.

Таким образом, tool.on('exit', (code) => { является асинхронным , но не обещание или асинхронность / ожидание.

Итак, перенос new Promiseв основном используется для преобразования этого в функцию стиля Promise, которую можно использовать как Promise или с async / await.


последнее слово об этом фрагменте:

for await (const data of tool.stdout) {
    return data
}

этонемного проблематично. Хотя потоки узлов являются асинхронными генераторами, они возвращаются после первого фрагмента и прерываются, как только поток получает несколько фрагментов. Поэтому вы должны либо заменить return на yield и вернуть асинхронный генератор, либо объединить буферы с циклом и return после завершения цикла.

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