benson00077 / dag_graph

playing around with DAG
0 stars 0 forks source link

[疑問] 關於 promise, async 寫一個 fake fetch 含 call back #7

Open benson00077 opened 3 years ago

benson00077 commented 3 years ago

背景解釋

想寫一個 fake fetch function 並在 fetch 之後呼叫 callback function,但不如預期

我想模仿 axios 寫一個 promise-based 的函數,如下

const fakeFetch = () => {
  return new Promise((res, rej)=> {
    console.log("data fetching...")
    setTimeout(() => {
      res("Execute after 0.3 sec")
    }, 300)
  })
    .then(res => console.log(res))
    .catch(err => console.log(err))
}  

且想在呼叫時利用 async 關鍵字,並避免 try ... catch chain,因此有個 HOF 如下

const asyncHandler = fn => {
  Promise
    .resolve(fn)
    .catch(err => console.log(err))
}

利用 async 關鍵字呼叫後,呼叫一個 call back,三種 test case 範例如下 (cb for callback function)

// --- 1 ---
async function myAsync1(options = {}, cb) {
  const executer = () => {
    if (options.two) fakeFetch()
  }

  if (options.one) await executer()

  if (typeof cb === "function") cb()
}

// --- 2 --- 
async function myAsync2(options = {}, cb) {
  const executer = async () => {
    if (options.two) await fakeFetch()
  }

  if (options.one) await executer()

  if (typeof cb === "function") cb()
}

// --- 3 --- 
async function myAsync3(options = {}, cb) {

  if (options.two) await fakeFetch()

  if (typeof cb === "function") cb()
}

// --- 用 HOF 包起來 ---
const test1 = (...args) => asyncHandler(myAsync1(...args))
const test2 = (...args) => asyncHandler(myAsync2(...args))
const test3 = (...args) => asyncHandler(myAsync3(...args))

最後是在別的檔案中呼叫這三個 test case ,如下

const options = {
  one: true,
  two: true
}

test1(options, console.log("This sentence show at the end"));
test2(options, console.log("This sentence show at the end"));
test3(options, console.log("This sentence show at the end"));

問題

  1. test1, 2 ,3 log 出的順序都是 "This sentence show at the end" → "data fetching..." → "Execute after 0.3 sec",我以為至少會是 "data fetching..." → "This sentence show at the end" 。原因是我的 fakeFetch() 有問題嗎?
  2. 如果要達到上點預期的順序,fakeFetch() 要怎麼改才好?
  3. 補充, 我自己寫用 axios 時 test 1 貌似本來就會達不到預期,test 2 可以。不太懂為什麼,也許等 fakeFetch() 寫好後比較好釐清這個問題 (先解決1.2問題後我再發問這點)。
dreamline2 commented 3 years ago
  1. 原因是因為你 callback 傳進去的不是一個 function 而是 console 所以直接執行。改成是 () => console.log("This sentence show at the end") 就會如預期是在最後 console
  2. 同上。補充兩點,第一點:在寫 promise function 的時候,你會預期是回傳 promise 因此會在 wrapper 做 chain 裡面就不使用 then, catch 第二點:asyncHandler 本身使用的 Promise.resovle 是讓 promise 確認回傳,因此不用接 catch,換句話說這一個 async handler 沒有其意義(?
  3. Method1 使用的 executer 是同步的,這意味者會直接執行。Method2 使用的 executer 是非同步的,這意味著會執行完在執行 callback function,因此 test1 不會達到預期是正常的
benson00077 commented 3 years ago

整理一下,也接續問一下。

釐清 Async, await 關鍵字

當我把 Test 修正為傳遞 cb 而不是呼叫之後

test1(options, () => console.log("This sentence show at the end"))
//  "data fetching..." → "This sentence show at the end" → "Execute after 0.3 sec"

test2(options, () => console.log("This sentence show at the end"));
//  "data fetching..." → "Execute after 0.3 sec" → "This sentence show at the end"

test3(options, () => console.log("This sentence show at the end"));
//  "data fetching..." → "Execute after 0.3 sec" → "This sentence show at the end"

到這裡我就不懂為何 myAsync1, myAsync3 會不一樣了。讀了一次 await 官方文件 後,我可不可以說:

async function myAsync2(options = {}, cb) {
  const executer = async () => {
    if (options.two) await fakeFetch()
  }

  if (options.one) executer()

  if (typeof cb === "function") cb()
}

test2(options, () => console.log("This sentence show at the end"));
//  "data fetching..." → "This sentence show at the end" → "Execute after 0.3 sec"

Err handler w/o try catch block

感謝你第二點的補充,有讓我稍微懂了些。

其他資料

dreamline2 commented 3 years ago
  1. 首先,需要先理解一件事情, async function 表示回傳是 promise,Test1 的 executer 不是 promise所以無法儲存狀態停止去等待它完成,因此變成先執行了。For your reference https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function#description
  2. 因為你寫的 asyncHandler 沒有回傳值。在文章中他是回傳一個 middleware 的函數,意味者他是可以把 try..catch..的錯誤包給下一個函數(next 是下一個函數的意思,因此不用設計這個東東)
benson00077 commented 3 years ago

補充 async await

  1. https://github.com/haizlin/fe-interview/issues/1310
  2. https://es6.ruanyifeng.com/#docs/async
benson00077 commented 2 years ago

補充 Generator , 跟 Promise & async await 關聯,也與協程概念關聯。 Generator 內的關鍵字 yield ,查了一下字典,這個字也有停車讓道、讓出放棄的意思, 像是 Generator 函數把執行所有權讓給別人先執行一樣,蠻有趣的。 參考:JavaScript中的协程