对于node中的async/await有几个问题请教
发布于 6 年前 作者 ProfutW 4201 次浏览 来自 问答

Q1:async/await和同步调用的区别?

比如我在一段代码中使用了await fs.readFileWithPromise(),这和直接使用fs.readFileSync()在效果上有什么区别?

Q2:redis的pub/sub机制和node创建的web服务器监听端口从而触发事件有什么区别?

//redis的例子
redis.subscribe('sleep');
redis.on('message', async (channel, msg) => {
	console.log(msg);
	await sleep(3000);
	console.log('end');
});
const sleep = ms => {
	return new Promise(resolve => {
		setTimeout(resolve, ms);
	});
};

上面这个例子,我在另一个地方向sleep这个频道多次pub消息的时候,并不会阻塞,都能即时地打印出msg; 但是下面这个http服务器表现不同:

const http = require('http');
http.createServer(async (req, res) => {
	if (req.url === '/favicon.ico') return;
	if (req.url === '/') {
		console.log('index');
		await sleep(3000);
		res.end('index');	
	}
	if (req.url === '/test') {
		console.log('test');
		await sleep(3000);
		res.end('test');	
	}
}).listen(3333);

当我请求’/‘时,在我没有得到响应之前,其他对’/‘的请求都会被挂起(服务端不打印’index’),但此时对’/test’的请求却能进入(打印’test’),为什么?如果线程被阻塞,那么应该都被挂起;如果没被阻塞,那么其他对’/'的请求为什么会被挂起?

最好能详细说一下它们在node中的执行流程(Event Loop层面)。 先在此谢过~~

24 回复

a1,readFileSync 会阻塞进程, await的话,只会阻塞 async 当前块的代码,外面的代码该异步还是异步。

@chenzeZzz 和我的想法是一样的,不过如果是这样 为什么第二个问题里http服务器会有那样的现象?我觉得如果同时并发请求可能都会有响应 就是你说的外异;但是我在主线程已经进入async函数处理(处于内同状态),到sleep定时器超时之前,再发出请求,照理说都会被阻塞呀,为什么对于不同的请求会有不同的现象呢?

来自酷炫的 CNodeMD

@ProfutW 上面最后写错了,在进入async函数后,设置定时器,然后返回,新的连接进来应该都能被处理,可是为什么第二次对/的请求却被挂起了?

来自酷炫的 CNodeMD

针对Q2 我认为不应该会出现这种情况,await是不会阻塞线程的,多个对’/'的请求服务端会立即打印index,至于你上述代码出现这种情况怀疑与http.createServer有关系,它支持async函数吗,或者不太兼容。建议用koa框架测试下。

来自酷炫的 CNodeMD

@lovegnep 好的,我待会试下koa里是什么表现;但是照理来说 async是ES7标准应该不存在http服务器不支持吧。。。

来自酷炫的 CNodeMD

@ProfutW node8.0 之后版本就自动支持 async 语法了

@ProfutW test立刻返回,说明/没有阻塞,你可以把sleep换成while true From Noder

Q2 用你的demo测试了一下,是能够正常处理的,和你描述的问题不一致。 node -v v9.2.0

@dkvirus 对呀 所以这个现象我很疑惑

@chenzeZzz 换成while我知道肯定是阻塞的 我的意思是await应该不会阻塞线程 但是我上面的例子呈现的是阻塞了

@miserylee 我刚才又试了一遍 我的描述应该没问题的 你可以把sleep时间换长一点 比如10S 然后你对’/'发出两次请求 你会发现第一次请求等待10秒 页面有响应之后,第二次请求才会进入被处理(打印index) 我的node是8.9.4的 这两个版本应该不会在这个问题上有区别吧

@ProfutW 刚才用KOA也试了下,两次访问的确是串行的,第二个客户等到第一个客户响应了之后才开始打印index,好奇怪.

const koa = require('koa');
const sleep = ms => {
	return new Promise(resolve => {
		setTimeout(resolve, ms);
	});
};
let app = new koa();
let num = 0;
app.use(async (ctx, next) => {
	num++;
	console.log('index',num);
	await sleep(10000);
	ctx.body = 'Hello World'+num;;
}).listen(3334);

@lovegnep 对呀 更奇怪的是 对其他url的访问却能够及时处理。。。

后台的console 显示每个请求都有 index/test, 没有被阻塞的

@lovegnep 我这里是正常的,两次请求都立刻打印index,node10.0.0,koa2.5.1

又试了几次,有时候会出现串行的情况,我再找找原因

@chenzeZzz 对不同url访问是不会阻塞 但是对同一个就会

@chenzeZzz @ProfutW

同一个页面刷新都会立刻打印index,两个页面同时请求只会打印第一个页面的index,此时刷新第一个页面就会立刻打印下一个index

奇怪的是3个页面请求时,断开第一个页面,剩下两个又会同时处理了。。。(难道是因为剩下两个在同一次事件循环内,所以可以被同时处理?

不是koa或async的问题,http就是这样的

const http = require('http');
let num = 0;
http.createServer(()=>console.log(num++)).listen(3333);

@dislido 的确是这样。。。我试了在定时器后面输出内容 结果是不停刷新后 10S后全部一起输出 这样的话await是没有阻塞线程 可是是什么阻塞了服务器响应呢 难道从tcp连接开始一直到超时这段时间 http服务器都不能响应其他请求? 但是为什么对另一个路径的请求却可以被处理呢?

@dislido 你是在同一个浏览器里做实验吗? 怀疑浏览器没有立刻发出第二个请求。我等会儿回去用手机和电脑实验下

来自酷炫的 CNodeMD

@lovegnep @dislido 应该是浏览器没有同时发出请求 我用curl同时发起两个请求 都能及时被接受并处理。至于@dislido 说的断开第一个连接 后面两个同时处理应该也是这个问题 断开第一个以后 浏览器同时发出了后面两个请求,所以没有被阻塞。。。看来浏览器在没有收到url的响应前是不会对同一个url发起多次请求的。。。

@ProfutW 嗯,浏览器优化请求。

来自酷炫的 CNodeMD

我也发现了这个问题了,还以为是我哪里用的不对。 看到大家的讨论,自己试了一下,确实是浏览器的优化啊,终于解决了我的困惑。 对于同一个地址的请求,如果前一个请求没返回,第二个也不会发起。

回到顶部