天ざるそばを作るとき、天ぷらを揚げてから、そばを茹でたら、天ぷらは冷えてしまう。そばを茹でてから天ぷらを揚げたら、そばにコシがなくなってしまう。できれば同時に作って、コシのあるそばでカラッとした天ぷらを食べたい。そんなわけでJavascriptでうまい天ざるそばを作る様子をシミュレートしてみよう。
ここで、天ざるそばのレシピを振り返ってみる。
A 天ぷらをつくる
(1) 素材準備 (5秒)
(2) 揚げる (10秒)
B そばを茹でる
(1) はかる (2秒)
(2) ゆでる (7秒)
(3) あらう (3秒)
C A,Bをもりつける (5秒)
手順としては、A->B->Cではなく、 (A|B)->C でなければ、いけない。
カッコ内は、仮の調理時間。
念のために、絵で確認しておこう。
準備
このシミュレーションでは、揚げたり、ゆでたりするそれぞれの作業を始めたときに「〜開始」、その作業が終わったときに「〜完了」と報告するようにしたい。
料理作業を次のように定義することで、要件を満たす。
まずはじめに、天ぷらをつくる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)を定義する。
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秒後に表示される
これらを順番に実行すれば、天ぷらが完成する。
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だ。
同様にして、そば手順を定義していく。
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を同時並行で実行させる部分は、次のように定義できる。
const tempra_and_soba = async () =>{
let t = tempra()
let s = soba()
await t
await s
}
これが終了した後に、もりつけを実行する。
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"
これで、うまい天ざるが食べられる。めでたし。
おまけ
お蕎麦屋さんを作ってみました。
お客さんが食べる時間などは省略。
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を使った動作サンプルでした。