原生node可以发起同步http请求吗?

习惯了php的代码顺序执行,最近要用node了,node的代码异步执行有些不习惯,在发起http请求这里遇到困难,php里是这样:

$htm=file_get_contents("http://localhost/emacs");
echo $htm;#不管是本地文件还是get、post请求、自定义header头等都用file_get_contents()解决

node在网上搜索到的各种发起http请求的办法都是异步的,请求得到数据后的操作全都是写到回调函数里面。这样有时候很有用,但有时候不是这样,代码逻辑就是主线程必须等待得到相关数据后才能进行下一步操作,且是回到主线程同步操作,否则就要改变程序的整体设计(比较麻烦)

目前的解决办法:使用node的child_process.execSync创建同步子进程,在子进程里用非node办法发起http请求得到数据缓存,node在子进程结束后在去取到子进程缓存的数据在进行下一步操作

不知道有没有更好的办法来解决这一问题

原生 node 的意思是你不想引入第三方 npm 包吗? 这样的话,可以 promisify 原生 http/https 的 request 方法,然后就可以用同步的方式(async/await)书写异步逻辑

另外,对 node api 不熟悉的话容易错失一些优化技巧,这方面要多读一读文档,不然原生写出来的性能还不如第三方依赖(所以为什么不用)

第三方包也用,只是感觉原生更好,但第三方包我也没找着发同步http请求的办法,async/await书写异步逻辑我有试过,可以写出同步执行的代码,但用于http请求好像还是同步不了,没测试成功

把异步 IO 降到完全同步,得不偿失,只要在代码逻辑上是同步就可以了。还是把焦点放在写法的问题上吧,可以贴个demo,看看你是怎么处理失败的?

建议主线程的地方提供一个callback。异步执行后直接调用callback。

node没有同步函数

要实现本地远程共用一个方法,大致是这样的:

代码没有调试过,大致的意思就是这样。


function File_get_contents(url, callback) {
    this.url = url;
    this.callback = callback;
    if (/file:\/\//.test(url)) {
        this.get_localfile()
    } else if (/http:\/\//.test(url)) {
        this.get_http_file()
    } else {
        this.callback("url not support")
    }
}
File_get_contents.prototype.get_local_file = function () {
    var file = this.url.replace(/file:\/\//, ''); // convert to local file path
    try (
        var data = require('fs').readFileSync(file).toString();
        this.callback(null, data);
    } catch (e) {
        this.callback(e);
    }
};
File_get_contents.prototype.get_http_file = function () {
    var req = require('http').get(this.url, function (res) {
        var data = '';
        res.on('data', function (chunk) {
            data += chunk;
        }.bind(this));
        res.on('end', function () {
            this.callback(null, data);
        }.bind(this));
    }.bind(this));
    req.on('error', function (err) {
        this.callback(err);
    }.bind(this));
};

new File_get_contents("file://path/to/localfile", function (err, data) {
    console.log(err, data);
});
new File_get_contents("http://host:port/path/to/file", function (err, data) {
    console.log(err, data);
});

async/await加原生的http/https 的 request 方法写没搞定,刚才试了下async/await加第三方包axios的写法可行

const axios = require('axios');
(async () => {
  try {
    const response = await axios.get('http://localhost/');
    console.log(response.data);
  } catch (error) {
  }
})();
console.log("这里得不到http请求的数据,要把主线程写在async里");

但这样也需要把程序的主线程写在async里,看来只能这样了

没毛病。一般只需要在入口文件把整个流程包在 async,然后把逻辑函数、模块拆分到其他文件,观感就好多了

佩服老哥有耐心现写代码,但这要需要考虑的因素太多了,比如多进程模型

我认为大部分 node 用户的需求只是想要写起来同步,所以还是直接推荐语法糖更友好一些

没有解决标题的问题。但是包装了原生的http.get给async/await用。

function get(u) {
    let handler = http
    if (u.startsWith('https://')) {
        handler = https
    }
    return new Promise((resolve, reject) => {
        handler.get(u, (res) => {
            res.setEncoding('utf8')
            let data = ''
            res.on('data', (chunk) => {
                data += chunk
            })
            res.on('end', () => {
                resolve(data)
            })
        }).on('error', err => {
            reject(err)
        })
    })
}

其他地方

await get("http://a.b.c")

如果一定要同步方式,那只能放弃node了。

node天生就是异步的。

其实如果真正掌握了异步的方法,写通信程序,比同步好用很多。

es6之后引入的await之类的方法,仅仅是让源代码看上去像是同步方式的代码。

但实际上还是异步方式的语法糖,同步异步的核心并不是代码的样子,而是代码执行的顺序。

这些写法实际上使问题变得更诡异。代码的书写顺序与执行顺序不一带来的理解难度只会比回调函数更高。

要用好Node,做好的方法是忘掉同步模式,按异步的方式思考,这样才能真正体会到Node的好处,发挥Node的优势。

因为网络通信本身就是异步的。Node天生适合干这个.

如你所说,都是语法糖而已。

这种执行都是异步的,只不过写起来像是同步的。 避免回调地狱,代码简洁,易于维护。 可能会有一些性能上的损失,没仔细测试,不多说。

let a = await getA()
let b = await getB(a)