• 记一次 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 了多个目标,一定要注意他们相互之间的影响。
    • 对于一项技术,唯有在透彻理解其机制后,才能更好的运用。
  • 相关阅读:
    [][]
    Spark笔记04
    Spark笔记03
    Spark笔记02
    Spark笔记01
    【熟能生巧】使用Screw快速生成数据库文档
    记一次关于jdbcTemplate.queryForList快速Debug及感悟
    【从零单排】Exception实战总结1
    【从零单排】Java性能排查实战模拟
    【从零单排】关于泛型Generic的一些思考
  • 原文地址:https://www.cnblogs.com/snadn/p/6524864.html
Copyright © 2020-2023  润新知