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


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

背景

在使用如下代碼發起請求的時候,個別接口出現了無法得到結果的情況。

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());
	}
}

追查

首先

我通過其他請求工具,發現出問題的接口是正常響應的。也就是確認了問題是出在自己的代碼里面的。

然后

我在 try 后面打斷點,想看一下 resData 收到的是什么,發現程序根本走不到那,但是 try 里面也沒有報錯。
到這一步,我就覺得問題有點奇怪了。

接下來

我只能到 node-fetch 的代碼里面去加斷點看一下是什么情況了。

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

通過進一步調試,我發現如果不對 node stream 的模型做一個系統的了解,我可能會很難查出問題的原因。但對於問題的解決,依稀記得之前使用 res.json() 的時候是沒有問題的。

嘗試解決

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

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

原因探究

后來,我又通過一步一步斷點調試和對 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 了多個目標,一定要注意他們相互之間的影響。
  • 對於一項技術,唯有在透徹理解其機制后,才能更好的運用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM