iT邦幫忙

0

Javascript 非同步疑問 (Node.js)

  • 分享至 

  • xImage

Hi all, 以下是我關於非同步的理解
非同步意義是在於不要讓程式blocking, 方法有Callback function, Promise, Async/Await等

假設前端要渲染畫面, 不能為了等待某個response而讓畫面卡住, 這時使用非同步方法, 先繼續渲染畫面, 等待response回來後呼叫callback function繼續動作(例如把data放上畫面)

如果現在要以第一筆response data當作第二筆request body(接續好幾個), 為了避免callback hell會使用Promise chain, 讓程式好讀好維護

後來出現async/await語法糖, 包裝後讓Promise更方便好用

使用async/await時, 例如:

async function getData() {
  const data1 = await promiseFn(1);
  const data2 = await promiseFn(2);
  // Do something
}

其中會先執行promiseFn(1), 待resolve後再執行promiseFn(2)
Q1: 此getData()為async function, 以剛剛前端例子來說, 我邊渲染畫面, 呼叫此函式到後端拿資料, 執行完後就可以Do something而不會讓畫面blocking, 這樣理解沒錯吧?

Q2: 現有個API功能是讓前端post data, server動作包含以下幾個:
(使用MongoDB, mongoose)

  1. 到DB搜尋是否有該筆資料
  2. 更新資料
  3. 儲存到DB
    簡化程式如下:
const foo = async function (req, res) {
    var result_data = await dataModel.find({/*cond*/}).exec();
    // ...
    result_data.DataA = new_Data
    
    await result_data.save();
    return res.status(200).json({message: "success"}});
};

問題在於, 執行到await時會等到執行結束才繼續往下, 這使得整個function像是同步執行了? 如果不加入async/await會有錯誤嗎? 目前想到若有多個前端可能會導致race condition?

補充Q3: 剛在stackoverflow看到一個發問,
連結:https://stackoverflow.com/questions/52832010/mongoose-await-save
但我找不出原PO的程式碼問題在哪, 還望指教~

app.post('/api/CreateUser', async (req, res) => {
    const newUser = new User({
        'email': req.body.email,
        'name': req.body.name
    });
    console.log('before save');
    await newUser.save((err, userDoc) => {
        if (err) return res.status(400).send(err);
        console.log('saved item');
    });
    console.log('after save');
});
/*
The current console.log order is:
before save
after save
saved item

But I would like it to be:
before save
saved item
after save
*/

謝謝

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 個回答

4
fillano
iT邦超人 1 級 ‧ 2021-11-18 13:10:08
最佳解答

Q1:

yes

Q2:

我的理解是:await時,執行中的context會進入suspend狀態,這時其他在event loop中的context就可以執行。而這些context結束時,如果原本的context結束suspend狀態,就會繼續執行下去。如果只著眼目前的context,他的確是停下來了。

解決race condition你得用其他方法,例如queue....async/await、promise、callback不是用來解決這個問題用的。

Q3:

原問題把saved item放在callback,而看一下mongoose的原始碼,這個callback是會在回傳資料前才用nextTick呼叫,所以會排在event loop中的next tick的queue裡面,等目前context執行完才跑,結果就是會在after save之後才出現。

看更多先前的回應...收起先前的回應...
阿薩姆 iT邦新手 4 級 ‧ 2021-11-18 14:19:05 檢舉

Q2:
context概念我可能還要再研究一下, 這與thread有關嗎, 記得JS是single thread?

Q3:
是否使用await後面就不該用callback function來寫啊, 感覺容易混淆

fillano iT邦超人 1 級 ‧ 2021-11-18 14:34:43 檢舉

Q2:
Execution Context,ECMA規格上面都是用這個詞。

Q3:
callback本來就不是設計給promise/async await用的,兩個混用才奇怪XD。promise在resovle時或async function在return時,都沒辦法也同時處理callback,因為結果丟進callback就沒了,繼續不下去。這是兩種不一樣的設計思維,也不應該混用的。

fillano iT邦超人 1 級 ‧ 2021-11-18 15:11:59 檢舉

建議找一下event loop相關的文章看看會比較清楚機制。

不過,瀏覽器跟node.js的event loop只有概念相同,機制細節也不完全一樣。node.js中nextTick的優先權較setTime/setInterval高,而瀏覽器裡根本沒有nextTick這個東西。

通常程式語言除非顯式地使用thread,不然也都是在single thread跑的啊,反而是javascript沒告訴你他的i/o或是網頁上的事件是在不同thread上觸發後排進event loop,然後跑你寫的callback。

froce iT邦大師 1 級 ‧ 2021-11-18 16:03:40 檢舉

node.js沒lock嗎?

fillano iT邦超人 1 級 ‧ 2021-11-18 16:19:16 檢舉

不知道npm上面有沒有...查了一下好像有,不過我沒用過XD

1
tunin
iT邦新手 4 級 ‧ 2021-11-19 16:55:58

async await 最終還是會變成 promise 來執行,而promise執行的程式會怎麼運作取決的node.js事件循環
對於事件循環,我覺得這篇文章描述的很清楚給你參考看看
https://notes.andywu.tw/2020/%E5%AE%8C%E6%95%B4%E5%9C%96%E8%A7%A3node-js%E7%9A%84event-loop%E4%BA%8B%E4%BB%B6%E8%BF%B4%E5%9C%88/

而需要注意的是 Promise 的 then 會被塞到一個優先權較高的 queue 中,當該 queue 有任務要執行時,會優先執行完才會重新輪詢 event loop,這部分可以實驗看看,所以適當的使用 setImmediate等方法可以避免因為一直建立太多高優先權的任務導致事件循中的其他queue無法被輪詢到的狀況。

我要發表回答

立即登入回答