記一次 node-fetch 使用時踩的坑

記一次 node-fetch 使用時踩的坑

背景

在使用以下代碼發起請求的時候,個別接口出現了沒法獲得結果的狀況。node

async function req() {
    const res = fetch(xxx);

    let resData = null;
    try {
        resData = await res.clone().json();
    } catch (err) {}

    if (!resData) {
        log(await res.clone().text());
    }
}

追查

首先

我經過其餘請求工具,發現出問題的接口是正常響應的。也就是確認了問題是出在本身的代碼裏面的。json

而後

我在 try 後面打斷點,想看一下 resData 收到的是什麼,發現程序根本走不到那,可是 try 裏面也沒有報錯。
到這一步,我就以爲問題有點奇怪了。緩存

接下來

我只能到 node-fetch 的代碼裏面去加斷點看一下是什麼狀況了。async

在這過程當中又出現了很詭異的一幕。我分別在 on('data')on('end') 的時候加調試信息,發現 end 事件沒有觸發,但若是在 on('data') 中添加斷點的話,end 可以觸發,而整個請求也能收到響應結果了。工具

經過進一步調試,我發現若是不對 node stream 的模型作一個系統的瞭解,我可能會很難查出問題的緣由。但對於問題的解決,依稀記得以前使用 res.json() 的時候是沒有問題的。fetch

嘗試解決

因而,我嘗試着將 res.clone().json() 改爲 res.json(),果真問題不在出現,請求順利接收。這時候我開始懷疑是否是 node-fetch 在 clone 的實現上有 bug 。但看了看源碼,思路很清晰,感受不出哪有問題啊。因此,沒有了解清楚 stream 相關的思路前,還不能妄下定論。調試

而對於 .json 失敗後,須要記錄響應文本的狀況,就改用 res._convert().toString() 實現了。code

緣由探究

後來,我又經過一步一步斷點調試和對 stream 的文檔和源碼的查看,終於定位了問題。接口

原來,node-fetch 在 clone 的時候產生了兩個目標,源碼以下:事件

p1 = new PassThrough();
p2 = new PassThrough();
body.pipe(p1);
body.pipe(p2);
// set instance body to teed body and return the other teed body
instance.body = p1;
body = p2;

而後個人代碼使用了其中一個即 res.clone 的返回進行 .json 操做,至關於 p2.json()。但對另外一個 res 即 p1 沒有作處理。
而 stream 有一個 back pressure 機制,由於 p1 沒有消耗,緩存數據滿時會使其源 pause,從而致使 p2 也不能結束。

結語

  • 使用 stream 時,若 pipe 了多個目標,必定要注意他們相互之間的影響。
  • 對於一項技術,惟有在透徹理解其機制後,才能更好的運用。
相關文章
相關標籤/搜索