Javascript で天ざるそばをつくる

### 
天ざるそばを作るとき、天ぷらを揚げてから、そばを茹でたら、天ぷらは冷えてしまう。そばを茹でてから天ぷらを揚げたら、そばにコシがなくなってしまう。できれば同時に作って、コシのあるそばでカラッとした天ぷらを食べたい。そんなわけでJavascriptでうまい天ざるそばを作る様子をシミュレートしてみよう。

ここで、天ざるそばのレシピを振り返ってみる。

A  天ぷらをつくる
　　(1) 素材準備 (5秒)
　　(2) 揚げる   (10秒)

B  そばを茹でる
　　(1) はかる　(2秒)
　　(2) ゆでる  (7秒)
　　(3) あらう  (3秒)

C  A,Bをもりつける (5秒)

手順としては、A->B->Cではなく、 (A|B)->C でなければ、いけない。
カッコ内は、仮の調理時間。
念のために、絵で確認しておこう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237824/d09274cf-e200-d2e6-8929-e2272f24458a.png)

### 準備

このシミュレーションでは、揚げたり、ゆでたりするそれぞれの作業を始めたときに「〜開始」、その作業が終わったときに「〜完了」と報告するようにしたい。
料理作業を次のように定義することで、要件を満たす。
まずはじめに、天ぷらをつくるA-(1)を定義する。

```Javascript:A-(1)
function prepare(resolve, cooking_time){
  console.log("てんぷら素材準備開始");
  setTimeout(()=>resolve("てんぷら素材準備完了+cooking_time"),cooking_time*1000)
}
function prepare_tempra(sec){return new Promise((resolve)=>{prepare(resolve,sec)});}
prepare_tempra(5).then(r=>console.log(r))
```
これを実行すれば、次のように出力される。

```
"てんぷら素材準備開始"  <- 実行直後に表示される
"てんぷら素材準備完了5"  <- 5秒後に表示される
```

次に、天ぷらをつくるA-(2)を定義する。

```Javascript:A-(2)
function fry(resolve, cooking_time){
  console.log("てんぷら揚げ開始");
  setTimeout(()=>resolve("てんぷら完了"+cooking_time),cooking_time*1000)
}
function fry_tempra(sec){return new Promise((resolve)=>{fry(resolve,sec)});}
fry_tempra(10).then(r=>console.log(r))
```

これを実行すれば、次のように出力される。

```
"てんぷら揚げ開始"  <- 実行直後に表示される
"てんぷら完了10"  <- 10秒後に表示される
```

これらを順番に実行すれば、天ぷらが完成する。

```Javascript:A
const tempra = async () => {
  await prepare_tempra(5).then(r=>console.log(r))
  await fry_tempra(10).then(r=>console.log(r))
}
```
これを実行すると、次のように出力される。

```
"てんぷら素材準備開始"  <- 実行直後に表示される
"てんぷら素材準備完了5"  <- 5秒後に表示される
"てんぷら揚げ開始"  
"てんぷら完了10"  <- 15秒後に表示される
```

ひとまず、天ぷら手順は、OKだ。
同様にして、そば手順を定義していく。

```Javascript:B
function measure(resolve, cooking_time){
  console.log("そばをはかる");
  setTimeout(()=>resolve("そば計量完了"+cooking_time),cooking_time*1000)
}

function boil(resolve, cooking_time){
  console.log("そばをゆでる");
  setTimeout(()=>resolve("そばゆで完了"+cooking_time),cooking_time*1000)
}

function wash(resolve, cooking_time){
  console.log("そばをあらう");
  setTimeout(()=>resolve("そばあらい完了"+cooking_time),cooking_time*1000)
}

function measure_soba(sec){return new Promise((resolve)=>{measure(resolve,sec)});}
function boil_soba(sec){return new Promise((resolve)=>{boil(resolve,sec)});}
function wash_soba(sec){return new Promise((resolve)=>{wash(resolve,sec)});}

const soba = async () => {
  await measure_soba(2).then(r=>console.log(r)) 
  await boil_soba(7) .then(r=>console.log(r))
  await wash_soba(3) .then(r=>console.log(r))
}
```

かなり、冗長ではあるが、ひとまず、A、Bが定義できた。
いよいよA、Bを同時並行で実行させたい。それで、両方とも終了したときに、C:もりつけを実行したい。
さて、どうするか？

A、Bを同時並行で実行させる部分は、次のように定義できる。

```Javascript:(A|B)
const tempra_and_soba = async () =>{
    let t = tempra()
    let s = soba()
    await t
    await s  
}
```

これが終了した後に、もりつけを実行する。

```Javascript:(A|B)->C
function serve(resolve, cooking_time){
  console.log("もりつける");
  setTimeout(()=>resolve("天ざるそばもりつけ完了"+cooking_time),cooking_time*1000)
}
function serve_temzarusoba(sec){return new Promise((resolve)=>{serve(resolve,sec)});}

const temprasoba = async () => {
await tempra_and_soba() 
await serve_temzarusoba(5) .then(r=>console.log(r))
}
```

実行結果

```
"そば計量完了2"
"てんぷら素材準備完了5"
"そばゆで完了7"
"そばあらい完了3"
"てんぷら完了10"
"天ざるそばもりつけ完了5"
```

これで、うまい天ざるが食べられる。めでたし。

### おまけ

お蕎麦屋さんを作ってみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237824/d6d9828b-b129-535f-4874-4952dc10d2af.png)

お客さんが食べる時間などは省略。

```Javascript:sobaya.js
function prepare(resolve, cooking_time){
  /*console.log("てんぷら素材準備開始");*/
  setTimeout(()=>resolve("てんぷら素材準備完了"+cooking_time),cooking_time*1000)
}

function fry(resolve, cooking_time){
  /* console.log("あげる") */;
  setTimeout(()=>resolve("てんぷら完了"+cooking_time),cooking_time*1000)
}

function measure(resolve, cooking_time){
  /* console.log("そばをはかる") */;
  setTimeout(()=>resolve("そば計量完了"+cooking_time),cooking_time*1000)
}

function boil(resolve, cooking_time){
  /* console.log("そばをゆでる") */;
  setTimeout(()=>resolve("そばゆで完了"+cooking_time),cooking_time*1000)
}

function wash(resolve, cooking_time){
  /* console.log("そばをあらう") */;
  setTimeout(()=>resolve("そばあらい完了"+cooking_time),cooking_time*1000)
}

function serve(resolve, cooking_time){
  /* console.log("もりつける") */;
  setTimeout(()=>resolve("もりつけ完了"+cooking_time),cooking_time*1000)
}

function open_shop(resolve, reject, sales_time){
  /* console.log("開店") */
  setTimeout(()=>{if (this.order==0) {resolve("お店終了"+sales_time)} else {reject("まだお客さんいるけど、お店終了"+sales_time)}},sales_time*1000)
}

function open_sobaya(sec){return new Promise((resolve,reject)=>{open_shop(resolve,reject,sec)});}

function prepare_tempra(sec){return new Promise((resolve)=>{prepare(resolve,sec)});}
function fry_tempra(sec){return new Promise((resolve)=>{fry(resolve,sec)});}
function measure_soba(sec){return new Promise((resolve)=>{measure(resolve,sec)});}
function boil_soba(sec){return new Promise((resolve)=>{boil(resolve,sec)});}
function wash_soba(sec){return new Promise((resolve)=>{wash(resolve,sec)});}
function serve_soba(sec){return new Promise((resolve)=>{serve(resolve,sec)});}

const tempra = async () => {
  await prepare_tempra(5).then(r=>console.log(r))
  await fry_tempra(10).then(r=>console.log(r))
}

const soba = async () => {
  await measure_soba(2).then(r=>console.log(r)) 
  await boil_soba(7) .then(r=>console.log(r))
  await wash_soba(3) .then(r=>console.log(r))
}

const tempra_and_soba = async () =>{
  let t = tempra()
  let s = soba()
  await t
  await s   
}

const temprasoba = async () => {
  await tempra_and_soba() 
  await serve_soba(5) .then(r=>console.log(r))
}

const zarusoba = async () => {
  await soba() 
  await serve_soba(5) .then(r=>console.log(r))
}

const sobaya = async () =>{
  console.log("開店")
  let shop = open_sobaya(90).then(r=>console.log(r)).catch(r=>console.log(r))
  
  console.log("一人目")
  order=1
  await temprasoba()
  order=0
  console.log("二人目")
  order=1
  await zarusoba()
  order=0
  console.log("三人目")
  order=1
  await temprasoba()
  order=0
}

var order=0
sobaya()
```

実行結果。

```
"開店"
"一人目"
"そば計量完了2"
"てんぷら素材準備完了5"
"そばゆで完了7"
"そばあらい完了3"
"てんぷら完了10"
"もりつけ完了5"
"二人目"
"そば計量完了2"
"そばゆで完了7"
"そばあらい完了3"
"もりつけ完了5"
"三人目"
"そば計量完了2"
"てんぷら素材準備完了5"
"そばゆで完了7"
"そばあらい完了3"
"てんぷら完了10"
"もりつけ完了5"
"お店終了90"
```

以上、Promiseを使った動作サンプルでした。