⌨️

async/await を利用した,タイプライター風自己紹介

2023/02/13に公開

はじめに

この度とある会社にエンジニアとして新卒入社するのに伴い,自己紹介シートの提出を求められました.せっかくならばエンジニア受けがよさそうなものをということで,タイプライター風に1文字ずつ自己紹介がコンソールに出力される小さなアプリケーションを作成したので,ついでに記事にしてみました.

対象

この記事は,非同期処理,Promise,async/awaitについてある程度知っている方を想定しているので,これらの用語を初めて聞くという方は以下の記事がわかりやすかったです.
https://qiita.com/soarflat/items/1a9613e023200bbebcb3

ソースコードの説明

まずは完成形を見てみましょう.アプリは以下になります.
https://replit.com/@So-san/ZennGong-You-Yong-SelfIntro
使い方はREADME.mdをご覧ください.

こちらにmyInfo.jsのコードを一つずつ説明していきたいと思います.

myInfo.js
const myInfo = {
  "名前": [
    "So-san",
    "そーさん"
  ],
  "趣味": [
    "読書(歴史・生物)",
    "プログラミング"
  ],
  "休日の過ごし方": [
    "おいしいごはんを食べる",
    "おもしろい本を読む",
    "プログラミングを頑張る"
  ]
}

const sleep = msec => new Promise(r => setTimeout(r, msec))

const display = async words => {
  for(const word of words.split("")){
    await sleep(100)
    process.stdout.write(word)
  }
}

const selfIntroduce = async () => {
  await display("はじめまして.So-sanです.\n")
  for(const key of Object.keys(myInfo)){
    await display("\n" + key + ")" + "\n")
    for(const val of myInfo[key]){
      await display("| " + val + "\n")
    }
  }
  await display("\nこれからよろしくお願いします!")
}

selfIntroduce()

sleep関数

引数

msec
処理をsleepさせるミリ秒数を指します.

戻り値

new Promise(r => setTimeout(r, msec))
Promiseオブジェクトを返します.

rはresolveを表しています.
また,setTimeoutが設定されているので,この関数はmsecミリ秒後にPromiseオブジェクトを返すことになります.

概要

引数で与えられたミリ秒数分処理を停止します.

ただ処理を停止するだけならば,以下のように空ループを回すという方法もあるのですが,

この方法ではsleep関数を実装していません.

sleep関数(空ループver.)
const sleep = msec => {
  const start = new Date()
  while (new Date() - start < msec) // 処理が停止するわけではなく,空処理が繰り返される
}

display関数

引数

words

戻り値

なし

概要

与えられた文字列を1文字ずつに分割して,順番に表示します.

この関数は=の後にasyncがついているので,Promiseを返す非同期処理の関数になります.
受け取った文字列wordsを1文字ずつ分割(split)し,1文字ずつwordという名前でfor文に渡しています.
なぜ非同期処理にするのかというと,その下にあるawait sleep(100)を使って処理の一時停止を実現したいからです.

ここで,sleepは与えられた引数msecミリ秒分待ってからPromiseを返すので,process.stdout.write(word)を実行する前に処理を一時停止することができます.process.stdout.write(word)console.log(word)のように与えられた引数をコンソールに出力しますが,console.log(word)と違い,最後に改行が入らないのが特徴です.今回は1文字ずつ出力するので,出力の度に改行が入ってしまうと縦書きのようになってしまい,望んでいる出力とは異なります.
これによって文字を1文字ずつ出力する(つまり,1文字出力するごとに一時停止する)ことを実現しています.

また,for文の部分をwords.split("").map(word => {~~~})とした方がすっきりするんじゃない?と思われる方もいるかもしれませんが,

これより,for文を使用しています

selfIntroduce関数

引数

なし

戻り値

なし

概要

自己紹介をタイプライター風に1文字ずつ表示します.

全てのdisplay関数の前にawaitがありますが,これにより,複数の非同期処理の関数を同期的に扱うことができています.
先ほど述べた通り,process.stdout.write(word)では末尾に改行は入らないので,適宜改行文字\nを入れる必要があります.

おわりに

以上で,タイプライター風自己紹介が実現できます.async/awaitの応用例として理解の役に立っていればいいなと思います.また,実際に自分なりの自己紹介コードを作成してみるのもいいかもしれません.

では,

参考

https://qiita.com/soarflat/items/1a9613e023200bbebcb3
https://stackoverflow.com/questions/73447580/sleep-function-during-map-in-async-await-not-working
https://replit.com/@So-san/ZennGong-You-Yong-SelfIntro

GitHubで編集を提案

Discussion