Node.js中的同步请求

时间:2012-01-08 03:56:53

标签: javascript node.js

如何让Node.js中的'request'模块以同步方式加载?我看到的最好的建议是以某种方式使用回调来使函数在完成之前不返回自身。我试图在代码中内联使用'request'函数(需要根据无法放回回调中的数据处理事物)。

那么我怎么能使用'request'模块的回调来阻止它自己返回,直到加载完资源为止?

我正在做的是运行一个从API下载两个值的循环,然后根据这些值进行一些数学运算。虽然可以在回调中完成数学运算......循环将在没有执行下一个操作所需的值的情况下前进。 (因此,阻止循环推进直到数据准备好将解决问题)

    /* loop */ {
         /* URL Generation */


    request( {url: base + u_ext}, function( err, res, body ) {
        var split1 = body.split("\n");
        var split2 = split1[1].split(", ");
        ucomp = split2[1];
    });

    request( {url: base + v_ext}, function( err, res, body ) {
        var split1 = body.split("\n");
        var split2 = split1[1].split(", ");
        vcomp = split2[1];
    });

    /* math which needs to be after functions get variables and before loop advances */
    }

9 个答案:

答案 0 :(得分:45)

  

简短的回答是:不要。 (...)你真的不能。这是一件好事

我想就此直截了当地说明:

NodeJS 支持Synchronous Requests。它的设计并不是为了支持它们开箱即用,但如果你足够敏锐,有一些解决方法,这里有一个例子:

var request = require('sync-request'),
    res1, res2, ucomp, vcomp;

try {
    res1 = request('GET', base + u_ext);
    res2 = request('GET', base + v_ext);
    ucomp = res1.split('\n')[1].split(', ')[1];
    vcomp = res2.split('\n')[1].split(', ')[1];
    doSomething(ucomp, vcomp);

} catch (e) {}

当您在“同步请求”中打开引擎盖时您可以看到库在后台运行同步child process。正如同步请求README中所解释的那样,它应该明智地使用 very 。这种方法锁定主线程,这对性能不利。

但是,在某些情况下,通过编写异步解决方案几乎没有或没有优势(与通过编写难以阅读的代码所做的某些伤害相比)。

这是许多HTTP请求库在其他语言(Python,Java,C#等)中的默认假设,并且该哲学也可以传递给JavaScript。语言毕竟是一种解决问题的工具,如果好处大于缺点,有时你可能不希望使用回调。

对于JavaScript纯粹主义者来说,这可能是异端邪说,但我是一个实用主义者,所以我可以清楚地看到,如果你发现自己遇到以下某些情况,使用同步请求的简单性会有所帮助:

  1. 测试自动化(测试通常是同步的)。

  2. 快速API混搭(即黑客马拉松,概念验证等)。

  3. 帮助初学者的简单示例(之前和之后)。

  4. 请注意,上面的代码应用于制作。如果你要运行一个合适的API然后使用回调,使用异步库,使用promises或其他,但避免同步代码,除非你想在服务器上浪费大量的CPU时间。

答案 1 :(得分:19)

在2018年,您可以在Node.js中使用asyncawait来编写“常规”样式。

下面是一个示例,该示例将请求回调包装在Promise中,然后使用await来获取已解析的值。

const request = require('request');

// wrap a request in an promise
function downloadPage(url) {
    return new Promise((resolve, reject) => {
        request(url, (error, response, body) => {
            if (error) reject(error);
            if (response.statusCode != 200) {
                reject('Invalid status code <' + response.statusCode + '>');
            }
            resolve(body);
        });
    });
}

// now to program the "usual" way
// all you need to do is use async functions and await
// for functions returning promises
async function myBackEndLogic() {
    try {
        const html = await downloadPage('https://microsoft.com')
        console.log('SHOULD WORK:');
        console.log(html);

        // try downloading an invalid url
        await downloadPage('http://      .com')
    } catch (error) {
        console.error('ERROR:');
        console.error(error);
    }
}

// run your async function
myBackEndLogic();

答案 2 :(得分:10)

简短的回答是:不要。如果您想要线性读取的代码,请使用像seq这样的库。但只是不要指望同步。你真的不能。这是一件好事。

很少或没有什么不能放回调。如果它们依赖于公共变量,请创建一个包含它们的闭包。什么是手头的实际任务?

你想要一个计数器,只在数据出现时才调用回调:

var waiting = 2;
request( {url: base + u_ext}, function( err, res, body ) {
    var split1 = body.split("\n");
    var split2 = split1[1].split(", ");
    ucomp = split2[1];
    if(--waiting == 0) callback();
});

request( {url: base + v_ext}, function( err, res, body ) {
    var split1 = body.split("\n");
    var split2 = split1[1].split(", ");
    vcomp = split2[1];
    if(--waiting == 0) callback();
});

function callback() {
    // do math here.
}

更新2018:node.js在最近的版本中支持async / await关键字,并且使用表示异步进程作为promise的库,您可以等待它们。通过程序可以获得线性,顺序的流程,等待时可以继续进行其他工作。它构造得非常好,值得一试。

答案 3 :(得分:6)

您应该查看名为async

的库

并尝试使用async.series调用您的问题。

答案 4 :(得分:5)

Aredridels答案相对较好(赞成它),但我认为它缺少循环等效。这应该可以帮到你:

同步代码等同于:

while (condition) {
  var data = request(url);
  <math here>
}
return result;

串行执行的异步代码:

function continueLoop() {
  if (!condition) return cb(result);
  request(url, function(err, res, body) {
    <math here>
    continueLoop()
  })
}
continueLoop()

答案 5 :(得分:4)

虽然异步样式可能是node.js的本质,但通常你不应该这样做,有时候你想要这样做。

我正在编写一个方便的脚本来检查API,并且不想搞砸回调。

Javascript无法执行同步请求,但C库可以。

https://github.com/dhruvbird/http-sync

答案 6 :(得分:2)

您可以使用retus发出跨平台同步HTTP请求:

const retus = require("retus");

const { body } = retus("https://google.com");
//=> "<!doctype html>..."

就是这样!

答案 7 :(得分:1)

请参见同步请求https://github.com/ForbesLindesay/sync-request

示例:

var request = require('sync-request');
var res = request('GET', 'http://example.com');
console.log(res.getBody());

答案 8 :(得分:0)

您可以执行与请求库完全相似的操作,但这是使用const https = require('https');const http = require('http');进行同步的,它们应该随节点一起提供。

这里是一个例子,

const https = require('https');

const http_get1 = {
    host : 'www.googleapis.com',
    port : '443',
    path : '/youtube/v3/search?arg=1',
    method : 'GET',
    headers : {
        'Content-Type' : 'application/json'
    }
};

const http_get2 = {
host : 'www.googleapis.com',
    port : '443',
    path : '/youtube/v3/search?arg=2',
    method : 'GET',
    headers : {
        'Content-Type' : 'application/json'
    }
};

let data1 = '';
let data2 = '';

function master() {

    if(!data1)
        return;

    if(!data2)
        return;

    console.log(data1);
    console.log(data2);

}

const req1 = https.request(http_get1, (res) => {
    console.log(res.headers);

    res.on('data', (chunk) => {
        data1 += chunk;
    });

    res.on('end', () => {
        console.log('done');
        master();
    });
});


const req2 = https.request(http_get2, (res) => {
    console.log(res.headers);

    res.on('data', (chunk) => {
        data2 += chunk;
    });

    res.on('end', () => {
        console.log('done');
        master();
    });
});

req1.end();
req2.end();