一直有个疑问,大伙是怎么判断nodejs里一个函数执行是同步的还是异步的?
发布于 5 年前 作者 tomoya92 5035 次浏览 来自 问答

用nodejs开发的时候,总会碰到一些莫明其妙的问题,排查到最后,才发现是异步搞的鬼

比如:

  • nodejs里的for循环是异步的吗?
  • 可以总结为带有回调的都是异步的吗?好像不能这样记,nodejs api里有个读取文件的方法就分为 readFile readFileSync,可气的是它们都是有回调的

有大佬能不吝赐教讲解一下吗?跪谢!

另外还有一个问题,循环一个集合有很多方法,for, each, forEach, map等,这些东西里哪些是nodejs内置的?哪些是jquery里的?哪些是es规范里的呢?它们都是同步的还是异步的呢?

还请原谅我这小白的问题,相信很多学习nodejs的朋友都有过这样的疑惑 :joy


补充:

虽然现在 ES 标准里加上了 async await 来实现同步操作,还有 Promise,但业务复杂了,代码还是会被各种回调弄的执行流程出问题

举个例子

写 js 的时候用的最多的应该就是回调了,回调里可以传很多个参数,简单的操作,这样写很方便,但业务复杂了,就不方便了,回调地狱也就是这样来的

这时候你可能会说不是有 Promise 吗,但用这货我觉得也就是把代码变的好看些,拿结果还是要靠 then 方法回调拿

到这你可能还会说,不是还有 async await 吗,这货确实不用从 then 函数的回调里拿数据了,但用 nodejs 多了就会发现,很多函数的调用的写法还是会用到回调,而且这时候还会在回调函数的前面加上个 async,我也是无语了,这不是又回到起点了吗,如下

it ('waitBody', async function() {
	await driver.sleep(500).wait('body', 30000).html().then(function(code) {
    	isPageError(code).should.be.false;
    })
})

当然也有用起来舒服的地方,比如 mongoose 的查询

const results = await UserModel.find({});

综上,难道就没有一个优雅的方法能让代码一行一行的执行吗,前一个结果执行完了,拿到结果再参与下一行代码的执行?

有人会说了,你上面不是说了 async await 了吗,它不就是这样用的吗?那为啥还要在回调的方法上用 async await 呢?总觉得有点换汤不换药,折腾来折腾去,还是离不了回调,但回调又会涉及到代码结构和流程控制上的问题

12 回复

兄弟貌似一脑子浆糊,需要加强基础 。 可以参考下 阮一峰 的相关博客文章呵呵

  • nodejs 里面的 for 是 ECMA 原生的命令,同步
  • 多数情况下带有回调函数的是异步,不过 readFile 的确是带有回调函数参数的同步,因为同步函数也能介绍回调函数参数。
  • for, each, forEach, map 这些都是 ECMA 规范里面的。 node.js 、浏览器各自实现功能。

兄弟,你是大佬假装的萌新吗?3000多分的人了不可能问这种问题吧?

@waitingsong 感谢,确实是一脑子浆糊,一直没有理清楚这里面的问题,确实要加强一下基础了。。感谢大佬指点

@Gitforxuyang 不是大佬,真萌新,分数都是早期水的 😂

我是这样理解的:

  1. 原生的循环类的一般都是同步堵塞的
  2. 自己写的方法,要看函数说明或注释,返回promise就可以用await,有callback参数就是回调, (如果不写注释是比较费劲的!)
  3. async函数就是内部允许用await调用其它方法并返回promise而已

如有不对,请指正

确实有很多新手一脑子浆糊,如果我们看到 fs.copyFileSync 那么就知道这个函数是同步的,对应的 fs.copyFile 是异步的。

如果我们看到一个函数,比如 fn_abc,我们怎么知道这个函数是同步还是异步呢?只能看文档或者代码,因为我们从签名中无法知道这个函数是同步还是异步。

回调只是一种写法,并不能区分是同步还是异步。Node.js 创建之初,既没有 yield,也没有 async/awai,唯一能用的异步处理方式就是回调。

我觉得以后的编码风格会慢慢变为:同步使用 callback,异步使用 async/awai。

@justjavac 感谢,看来写nodejs还是要靠经验呀,用的多了,就知道了,也就用的顺手了

@tomoya92 我觉得 jjc 大神说的没有你那层意思,是你自己总结的… 还是 js 异步特性没有掌握,说明用得少,木有深入了解 js 异步原理,加油呀

@DevinXian 嗯,还是用的少了 :joy

这段代码也可以用的更好

it ('waitBody', async function() {
	await driver.sleep(500).wait('body', 30000).html().then(function(code) {
    	isPageError(code).should.be.false;
    })
})

可以先从内部优化

it ('waitBody', async function() {
    let getCode = await driver.sleep(500).wait('body', 30000).html();
    let code = await getCode;
    isPageError(code).should.be.false;
})

最后把 it优化掉,这样就不会回调里面套异步了(这步开始不要照搬,要看你的情况而定来优化it的回调)

let waitBody =  new Promise((reslove,reject)=>{
    it ('waitBody', function() {
        reslove(true)
    })
})

(async function () {
    await waitBody;
    let getCode = await driver.sleep(500).wait('body', 30000).html();
    let code = await getCode;
    isPageError(code).should.be.false;
})()

如果不希望it自执行,可以再包一层

let waitBodyFunc = function () {
    return new Promise((reslove,reject)=>{
        it ('waitBody', function() {
            reslove(true)
        })
    })
}

(async function () {
    await waitBodyFunc();
    let getCode = await driver.sleep(500).wait('body', 30000).html();
    let code = await getCode;
    isPageError(code).should.be.false;
})()

你可以把最底层的方法都向上包,这样你上层调用,就能像mongose那样愉快了

看返回值是否为Promise对象

@zy445566 @151263

要用 async await 的话,被await标记执行的方法返回值必须要是Promise对象,如果不是的话,也可以使用 nodejs 内置的一个函数来包装一下 util.promisify(func) ,不过这个方法包装的函数有个要求,就是被包装的函数的回调第一个要是error对象

然后就可以继续使用async/await了

回到顶部