System

Callback, Promise, Async/Await in Javascript

မင်္ဂလာပါ။
ကျွန်တော်ကတော့ Spiceworks Myanmar မှာ Backend Developer အနေနဲ့ တာဝန်ယူလုပ်ကိုင်နေတဲ့ သုတယာမိုး ဖြစ်ပါတယ်။ ဒီတစ်ခေါက်မှာတော့ callback, promise နှင့် async/await အကြောင်းကို ပြောပြပေးသွားမှာဖြစ်ပါတယ်။
ပထမဆုံးအနေနဲ့ Synchronous နှင့် Asynchronous အကြောင်းကို ပြောပြပေးချင်ပါတယ်။ ဥပမာအားဖြင့် process1, process2 နှင့် process3 ဆိုပြီးရှိတယ်ဆိုပါစို့။ Synchronous နည်းအရဆိုရင် process1 အလုပ်လုပ်ပြီးတဲ့အချိန်ကိုစောင့်မယ်။ ပြီးတော့မှ process2 အလုပ်လုပ်မယ်။ process2 ပြီးမှ process 3 အလုပ်လုပ်မယ် စသဖြင့် တစ်ခုပြီးမှ နောက်တစ်ခုကို ဆက်လုပ်မယ့် ပုံစံဖြစ်ပါတယ်။ Asynchronous ကတော့ ထိပ်ဆုံး process အလုပ်လုပ်ပြီးတဲ့ အချိန်ကို စောင့်မနေစေဘဲ process1, process2 နှင့် process3 စတဲ့ process သုံးခုလုံး ပြိုင်တူ အလုပ်လုပ်တဲ့ ပုံစံဖြစ်ပါတယ်။

//synchronous
const wait = (ms) => {
  let start = new Date().getTime();
  let end = start;
  while(end < start + ms) {
     end = new Date().getTime();
  }
}
const process1= () => {
  wait(3000)
  console.log(1)
}
const process2 = () => {
  wait(2000)
  console.log(2)
}
const process3 = () => {
  wait(1000)
  console.log(3)
}
process1()
process2()
process3()

Synchronous အရ process တစ်ခုပြီးမှ နောက်တစ်ခုကို ဆက်လုပ်မှာဖြစ်တဲ့အတွက် process ၃ခုလုံးပြီးဆုံးဖို့အတွက်ဆိုရင် စုစုပေါင်းကြာချိန် ၆စက္ကန့်ကြာမြင့်မှာဖြစ်ပါတယ်။

//asynchronous
const process1 = () => {
  console.log(1)
}
const process2 = () => {
  console.log(2)
}
const process3 = () => {
  console.log(3)
}
setTimeout(process1, 3000)
setTimeout(process2, 2000)
setTimeout(process3, 1000)

Asynchronous မှာဆိုရင်တော့ process တစ်ခုပြီးတဲ့အချိန်ကို စောင့်စရာမလိုတဲ့အတွက် process ၃ခုလုံးပြီးဆုံးဖို့အတွက်ဆိုရင် စုစုပေါင်းကြာချိန် ၃စက္ကန့်သာကြာမြင့်မှာဖြစ်ပါတယ်။

Callback, Promise and async/await တွေကတော့ Asynchronous operations တွေကို လုပ်ဆောင်ရာမှာ အသုံးပြုတဲ့ နည်းလမ်းများဖြစ်ပါတယ်။ Callback, Promise and async/await တို့ကို ဘောလုံးကန်ဖို့ ပြင်ဆင်တဲ့ ဥပမာအနေနဲ့ ရှင်းပြသွားပါမယ်။

playFootball()
callFriends()
rentField()
replyFriends()
comeField()
startPlaying()

ဘောလုံးကန်ဖို့အတွက် စတင်အကြံရမယ် (playFootball)၊ လူစုမယ်(callFriends)၊ ကွင်းငှားမယ်(rentField)၊ ကွင်းငှားလို့ရတဲ့အကြောင်းကို သူငယ်ချင်းတွေဆီပြန်ပြောမယ်(replyFriends)၊ ကွင်းကိုလာမယ်(comeField) နှင့် ဘောလုံးစကန်မယ်(startPlaying) ဆိုပြီး operation 6 ဆင့်ရှိတယ်ဆိုပါစို့။

Callback
Callback သည် အခြား function တစ်ခုဆီသို့ argument အနေနဲ့ ပေးပို့နိုင်သည့် function ဖြစ်ပါတယ်။ ပထမ function က ရလာတဲ့ ရလဒ်ကိုကြည့်ပြီး နောက်ထပ် function တစ်ခုကို ဆက်ပြီးအလုပ်လုပ်စေချင်တဲ့အခါ callback ကိုအသုံးပြုကြပါတယ်။

const playFootball = (callback) => {
  console.log('thinking what to do')
  const result = { error: false, success_msg: 'got an idea to play football', fail_msg: 'no idea' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const callFriends = (callback) => {
  console.log('to call friends')
  const result = { error: false, success_msg: 'to call friends' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const rentField = (callback) => {
  console.log('to rent field')
  const result = { error: false, success_msg: 'to rent field' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const replyFriends = (callback) => {
  console.log('to reply friends')
  const result = { error: true, success_msg: 'to reply friend' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const comeField = (callback) => {
  console.log('to come field')
  const result = { error: false, success_msg: 'to come field' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const startPlaying = (callback) => {
  console.log('to start playing')
  const result = { error: false, success_msg: 'to started playing' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const start = () => {
  playFootball((res1) => {
    if (!res1.error) {
      console.log(`(success) ${res1.success_msg}`)
      callFriends((res2) => {
        if (!res2.error) {
          console.log(`(success) ${res2.success_msg}`)
          rentField((res3) => {
            if (!res3.error) {
              console.log(`(success) ${res3.success_msg}`)
              replyFriends((res4) => {
                if (!res4.error) {
                  console.log(`(success) ${res4.success_msg}`)
                  comeField((res5) => {
                    if (!res5.error) {
                     console.log(`(success) ${res5.success_msg}`)
                      startPlaying((res6) => {
                        if (!res6.error) {
                          console.log(`(success) ${res6.success_msg}`)
                        } else {
                          console.log(`(fail) ${res6.success_msg}`)
                        }
                      })
                    } else {
                      console.log(`(fail) ${res5.success_msg}`)
                    }
                  })
                } else {
                  console.log(`(fail) ${res4.success_msg}`)
                }
              })
            } else {
              console.log(`(fail) ${res3.success_msg}`)
            }
          })
        } else {
          console.log(`(fail) ${res2.success_msg}`)
        }
      })
    } else {
      console.log(`(fail) ${res1.fail_msg ?? res1.success_msg}`)
    }
  })
}
start()

အထက်ပါ ဥပမာမှာဆိုရင် playFootball() function ကို စခေါ်တယ်။ ထို function မှာ error: false ဖြစ်တယ်ဆိုမှ callFriends() function ကိုဆက်ခေါ်ပြီး callFriends() function မှ ရလာတဲ့ result ဖြစ်တဲ့ လူစုလို့ရလား၊ မရလား ဆိုတဲ့ အခြေအနေကိုကြည့်ပြီး ကွင်းငှားသင့်၊ မငှားသင့်ကို လုပ်ဆောင်မှာဖြစ်ပါတယ်။ လူစုလို့ရခဲ့ရင် ကွင်းငှားမည့် လုပ်ငန်းစဥ်ကို ဆက်လုပ်မှာဖြစ်ပြီး လူစုလို့မရခဲ့ရင်တော့ ထိုအဆင့်မှာပဲ ရပ်သွားမှာဖြစ်ပါတယ်။ callback ကို အသုံးပြုတဲ့အခါ နည်းရင်ကိစ္စမရှိပေမယ့် များလာတဲ့အခါ code တွေရှုပ်ထွေးပြီး ဖတ်ရ၊ ပြင်ရခက်ခဲတဲ့ callback hell တစ်ခုဖြစ်လာနိုင်ပါတယ်။ ထို callback hell ပြဿနာကိုတော့ promise နှင့် async/await တို့ကို အသုံးပြုကာဖြေရှင်းနိုင်ပါတယ်။

Promise
Promise တွင် pending, fulfilled, rejected ဟူ၍ အခြေအနေသုံးမျိုး ဖြစ်နိုင်ပါတယ်။ Promise သည်လက်တွေ့ဘဝက ကတိပေးတာနဲ့တူပါတယ်။ အခြားသူမှ မိမိဆီ ကတိတောင်းလာတဲ့အခါ စဥ်းစားနေတဲ့ အချိန်သည် pending အခြေအနေဖြစ်ပါတယ်။ fulfilled ကတော့ လက်ခံတဲ့ အခြေအနေဖြစ်ပြီး rejected ကတော့ ငြင်းပယ်သည့် အခြေအနေဖြစ်ပါတယ်။ Promise((resolve, reject) => {}) မှာ မြင်တွေ့ရတဲ့ resolve ကတော့ promise တစ်ခု လက်ခံတဲ့အခါ အသုံးပြုရမယ့် function တစ်ခုဖြစ်ပြီး reject ကတော့ ငြင်းပယ်လိုက်တဲ့အခါ အသုံးပြုရမယ့် function တစ်ခုဖြစ်ပါတယ်။ resolve function ကနေ ပြန်လည်ပေးပို့လာတဲ့ value ကို .then() method ဖြင့်လက်ခံပြီး reject function ကနေ ပြန်လည်ပေးပို့လာတဲ့ result ကိုတော့ .catch() method ဖြင့် လက်ခံရမှာဖြစ်ပါတယ်။ .finally() method ကတော့ promise တစ်ခု အောင်မြင်တယ်၊ ကျရှုံးတယ် ဆိုပြီး အဖြေတစ်ခုခုရတဲ့အခါ အသုံးပြုနိုင်တဲ့ method ဖြစ်ပါတယ်။ ဥပမာ pending state တုန်းက button တစ်ခုကို နှိပ်လို့မရအောင် disabled လုပ်ထားခဲ့တယ်ဆိုရင် finally() method မှာ ထို button ကို နှိပ်လို့ရအောင် ပြန်ပြီး enable ပြန်လုပ်တဲ့ လုပ်ဆောင်ချက်မျိုးတွေကို လုပ်နိုင်ပါတယ်။ Promise ရဲ့ အားသာချက်ကတော့ .then()၊ .catch() အစရှိတဲ့ chain methods တွေကို အသုံးပြုနိုင်တဲ့အတွက် callback ကဲ့သို့ ရှုပ်ထွေးမနေဘဲ ဘာပြီးရင် ဘာလုပ်မယ်ဆိုတာကို ရှင်းရှင်းလင်းလင်း မြင်နိုင်မှာဖြစ်ပါတယ်။

const playFootball = (callback) => {
  console.log('thinking what to do')
  const result = { error: false, sucess_msg: 'got an idea to play football', fail_msg: 'no idea' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const callFriends = (callback) => {
  console.log('to call friends')
  const result = { error: false, sucess_msg: 'to call friends' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const rentField = (callback) => {
  console.log('to rent field')
  const result = { error: false, sucess_msg: 'to rent field' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const replyFriends = (callback) => {
  console.log('to reply friends')
  const result = { error: false, sucess_msg: 'to reply friend' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const comeField = (callback) => {
  console.log('to come field')
  const result = { error: false, sucess_msg: 'to come field' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const startPlaying = (callback) => {
  console.log('to start playing')
  const result = { error: false, sucess_msg: 'to started playing' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const promise = (callback) => {
  return new Promise((resolve, reject) => {
    callback((res) => {
      if (!res.error) {
        console.log(`(success) ${res.sucess_msg}`)
        resolve()
      } else {
        reject(`(fail) ${res.fail_msg ?? res.sucess_msg}`)
      }
    })
  })
}
const start = () => {
  promise(playFootball).then(() => promise(callFriends))
  .then(() => promise(rentField))
  .then(() => promise(replyFriends))
  .then(() => promise(comeField))
  .then(() => promise(startPlaying))
  .catch(error => console.log(error))
  .finally(() => console.log('resolve or reject. It comes to an end.'))
}
start()

Async/Await
Async/Await ကတော့ asynchronous operation တွေမှာအသုံးပြုဖို့အတွက် javascript ကနေ အသစ်ထည့်သွင်းထားတဲ့ နည်းလမ်းတစ်ခု ဖြစ်ပါတယ်။ သူ့ကို အသုံးပြုမယ်ဆိုရင်တော့ မိမိအသုံးပြုမယ့် function ရဲ့ အရှေ့မှာ “async” keyword ကိုထည့်သွင်းပေးရပါမယ်။ “async” keyword ထည့်ခြင်းအားဖြင့် ထို function ကို ခေါ်တဲ့အခါ promise တစ်ခုကို ပြန်လည်ပေးပို့ပေးမှာ ဖြစ်ပါတယ်။ await ကတော့ promise တစ်ခု fulfilled ဖြစ်သွားလား၊ rejected ဖြစ်သွားလားကို စောင့်ကြည့်ပြီး နောက်ထပ်လုပ်ငန်းစဥ်တွေ လုပ်သင့်၊ မလုပ်သင့်ကို handle လုပ်ပေးတဲ့ keyword ဖြစ်ပါတယ်။

const playFootball = (callback) => {
  console.log('thinking what to do')
  const result = { error: false, sucess_msg: 'got an idea to play football', fail_msg: 'no idea' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const callFriends = (callback) => {
  console.log('to call friends')
  const result = { error: false, sucess_msg: 'to call friends' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const rentField = (callback) => {
  console.log('to rent field')
  const result = { error: false, sucess_msg: 'to rent field' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const replyFriends = (callback) => {
  console.log('to reply friends')
  const result = { error: false, sucess_msg: 'to reply friend' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const comeField = (callback) => {
  console.log('to come field')
  const result = { error: false, sucess_msg: 'to come field' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const startPlaying = (callback) => {
  console.log('to start playing')
  const result = { error: false, sucess_msg: 'to started playing' }
  setTimeout(() => {
    callback(result)
  }, 3000)
}
const promise = (callback) => {
  return new Promise((resolve, reject) => {
    callback((res) => {
      if (!res.error) {
        console.log(`(success) ${res.sucess_msg}`)
        resolve()
      } else {
        reject(`(fail) ${res.fail_msg ?? res.sucess_msg}`)
      }
    })
  })
}
const start = async() => {
  try {
    await promise(playFootball)
    await promise(callFriends)
    await promise(rentField)
    await promise(replyFriends)
    await promise(comeField)
    await promise(startPlaying)
  } catch(error) {
    console.log(error)
  } finally {
    console.log('resolve or reject. It comes to an end.')
  }
}
start()

အခုဆိုရင်တော့ callback, promise နှင့် async/await တို့အကြောင်းကို နားလည်သွားမယ်လို့ ထင်ပါတယ်။ အဆုံးထိဖတ်ရှုပေးတဲ့အတွက် ကျေးဇူးတင်ပါတယ် ခင်ဗျ။

Hello

Leave a Reply

Your email address will not be published. Required fields are marked *