電子冒険:エピソード15:非同期コマンド実行


我々が作成したターミナルアプリはかなりの数の問題を抱えています.その最大のものは、コマンドを実行して完了するまでハングアップすることです.
第二の大きな問題は、我々が得る任意のエラーは、現在、アプリケーションに渡されていません.
我々は、エピソード13からsvelteターミナルアプリを起動し、必要な部分を変更します.

十分でないアプローチ


今までやってきたことは
function onsubmit(command) {
  let output = window.api.runCommand(command)
  history.push({command, output})
  history = history
}
ここでは、asyncコマンドの実行方法を考えてみましょう.
async function onsubmit(command) {
  let output = await window.api.runCommand(command)
  history.push({command, output})
  history = history
}
我々は、コマンドを完了するのを待つことができるし、結果を歴史にプッシュします.フロントエンドはブロックしないので、それは改善です、しかし、それはまだ奇妙に振る舞うでしょう-コマンドユーザが入ったコマンドは完全に消えるでしょう、そして、されるときまで、突然、その出力と共に再び現れてください.

ベターアプローチ


我々がする必要があることは2つのステップに従うことです-最初にコマンドが実行されている歴史のエントリを置きます.その後、そのエントリを変更してコマンドの出力を一度実行します.
APIを再実行しているので、必要に応じて残りのフィールドを含めることもできます.
  async function onsubmit(command) {
    let entry = {command, stdout: "", stderr: "", error: null, running: true}
    history.push(entry)
    history = history

    Object.assign(entry, {running: false}, await window.api.runCommand(command))
    history = history
  }
Object.assignはインデックスのフィドルより便利だった.あなたが混乱している場合には、history = historyは、私たちがそれを再割り当てしていないにもかかわらず、history変数が変更されたことをSvelteに伝える我々の方法です.最初は少し愚かなようですが、この「機能的な」バージョンはもっと冗長です.

プリロードの新しいRunCommand。js


ノードのasync APIは約束をしません.幸運にも約束をまとめるのは簡単です.
let runCommand = (command) => {
  return new Promise((resolve, reject) => {
    child_process.exec(command, (error, stdout, stderr) => {
      resolve({stdout, stderr, error})
    })
  })
}

フォントをインストールする


今、私たちは必要とするすべての情報を表示するためにsrc/HistoryEntry.svelteを変更する必要があります.私はそのコマンドがまだ走っていることを示したいです、しかし、どうにかHTML 5はまだ<spinner>タグをビルトイン持っていません.完全に邪魔をする、それはそのような普遍的なものです.
そのためには、devサーバを再起動してください.
$ npm i --save svelte-awesome

src / historyエントリ。スベルト


まず、font-awesomeから関連するアイコンをインポートする必要があります.
<script>
  import Icon from "svelte-awesome"
  import { spinner, exclamationTriangle } from "svelte-awesome/icons"

  export let command, stdout, stderr, error, running
</script>
したがって、以前のcommandおよびstdoutに加えて、stderrおよび2つのフラグerrorおよびrunning(ウェルerrorは実際にはフルエラーメッセージですが、存在するかどうかチェックするだけです).
<div class='history-entry'>
  <div class='input-line'>
    <span class='prompt'>$</span>
    <span class='input'>{command}</span>
  </div>
  <div class='stdout'>{stdout}</div>
  <div class='stderr'>{stderr}</div>
  {#if running}
    <Icon data={spinner} pulse />
  {/if}
  {#if error}
    <Icon data={exclamationTriangle} />
  {/if}
</div>
そして最後にいくつかのCSSは、少し前から調整します.
<style>
  .history-entry {
    padding-bottom: 0.5rem;
  }

  .stdout {
    color: #afa;
    white-space: pre;
  }

  .stderr {
    color: #faa;
    white-space: pre;
  }

  .input-line {
    display: flex;
    gap: 0.5rem;
  }

  .input {
    color: #ffa;
    flex: 1;
  }
</style>

結果


結果は以下の通りです.

これは、いくつかのサービス可能なターミナルアプリです.これはエラーが表示され、コマンドがまだ実行されているときに表示されます.主な問題は、コマンドが何かを表示する前に完全に終了するのを待つことです.次のエピソードでこの問題に取り組むことができます.
平常通りall the code for the episode is here.