【问题标题】:How to properly implement async or await operation in the loop - nodejs如何在循环中正确实现异步或等待操作 - nodejs
【发布时间】:2018-04-11 22:57:17
【问题描述】:

我是 nodejs 的新手,所以如果我错过了明显的内容,请原谅。 我目前正在为我的代码的异步操作而苦苦挣扎。它是这样的。我运行 express 框架来调用特定的 URL,该 URL 调用外部 REST 服务器以获取有关对象的一些信息。我处理 REST 响应、解析 JSON 并将信息传递给 EJS 视图引擎以呈现 HTML 页面的基本操作没有问题。 但是,我在一个特定的过程中遇到问题,在我得到 REST 响应(卷列表)之后,我想运行另一个 REST 调用以获取有关每个卷的更多详细信息(循环中)。检索完成后,我想调用 EJS 来呈现包含卷列表及其详细信息的页面。 这是我的代码:

function listVolumes(req, res) {
        var element = 'volume';
        var element_list= [];
        var output_json = [];
        var options = { method: 'GET',
                url: MY_ENV.MY_URL + '/v2/volumes',
          headers: 
           { 
             'Cache-Control': 'no-cache',
             'X-Auth-Token': MY_ENV.MY_TOKEN,
             'Content-Type': 'application/json',
             'Accept': 'application/json' }
        }
            request(options) // using request-promise
                .then(function (body) {
                output_json = JSON.parse(body); // parsing array of JSON objects from REST server
                if (Object.keys(output_json).length > 0){
                    for (var i in output_json.list) { // iterating over list of JSON objects
                        var volume_id = output_json.list[i];
                        console.log(i, ': ' , JSON.stringify(volume_id, null, 3));
                        getVolumeDetails(volume_id, function callback(volume){ // getting volume details
                            element_list.push({ id: volume_id, name: volume.name, size: volume.size, state: volume.state });
                        });
                    }
                    res.render('listVolumes', {element, element_list, output_json }); // calling ejs view-engine to render html page
                } else console.log('No volumes found.');
            })
                .catch (function (error) {
                    console.log('Got error while listing ' + element , error);
                });
}
function getVolumeDetails(volume_id, callback){
    var options = { method: 'GET',
            url: MY_ENV.MY_URL + '/v2/volumes/' + volume_id,
      headers: 
       { 
         'Cache-Control': 'no-cache',
         'X-Auth-Token': MY_ENV.MY_TOKEN,
         'Content-Type': 'application/json',
         'Accept': 'application/json' } };
    request(options)
        .then(function (body) {
            volume = JSON.parse(body);
            console.log('got volume: ' , volume.name , volume.size, volume.state);
            callback(volume);
        })
        .catch (function (error) {
        console.log('Got error while reading volume: ' + volume_id , error.message);
        }); 

}
app.get('/list/volumes', listVolumes);

很明显,当我调用 EJS 时,它会呈现 HTML 页面,然后由于 javascript 的异步特性,我会收到 getVolumeDetails() 的控制台消息。 我已经阅读了许多关于使用 request-promise、async.each、async wait、回调的文章,但仍然难以理解如何使其工作。我不介意等待几秒钟让循环完成获取所有卷的详细信息,用我想在 HTML 页面上显示的项目数组填充我的 element_list。 您能帮我指导使用什么构造以及如何使用吗?

谢谢。

【问题讨论】:

  • 你检查过async.queue吗?你也可以试试递归。
  • 我做了一些更改并且能够 async.each() 工作,但我发现它永远不会完成,即在所有迭代函数都运行后永远不会运行第三个函数。知道为什么吗?

标签: node.js


【解决方案1】:

这是我的修改:

function listVolumes(req, res) {
    var element = 'volume';
    var element_list= [];
    var output_json = [];
    var options = { method: 'GET',
            url: MY_ENV.MY_URL + '/v2/volumes',
      headers: 
       { 
         'Cache-Control': 'no-cache',
         'X-Auth-Token': MY_ENV.MY_TOKEN,
         'Content-Type': 'application/json',
         'Accept': 'application/json' }
    }
    request(options)
        .then(function (body) {
        output_json = JSON.parse(body);
        console.log('Raw body of listVolumes(): \n' , body);
        if (Object.keys(output_json).length > 0){
            async.each(output_json.list, function (volume_id, callback){
                options.url = MY_ENV.MY_URL + '/v2/volumes/' + volume_id;
                request(options)
                    .then(function (body) {
                        var volume = JSON.parse(body);
                        console.log('got callback: ' , volume.id, volume.name , volume.size, volume.state);
                        element_list.push({ id: volume.id, name: volume.name, size: volume.size, state: volume.state });
                        callback(null);
                    })
                    .catch (function (error) {
                    console.log('Got error while reading volume: ' + volume_id , error.message);
                    });
            }, 
            function (err){
                if (err) {
                    console.log('Error occured: ', err);
                } else {
                    console.log('All done...Element list length: ', element_list.length); // this never runs... why???
                }
                res.render('listVolumes', {element, element_list, output_json });
            });
        } else console.log('No volumes found.');
    })
    .catch (function (error) {
        console.log('Got error while listing ' + element , error);
    });
}

所以它永远不会运行函数(错误)中的代码,我在其中添加了注释 // this never runs... why???

【讨论】:

  • 我发现其中一个异步调用失败了,所以它没有触发最后一个回调()。所以我在处理错误后添加了另一个回调()并且确实完成了它。 .catch (function (error) { callback(); });
  • 所以我猜需要回调来处理错误?
  • 是的!没有那个回调,它不会完成并且永远不会调用渲染引擎
猜你喜欢
  • 1970-01-01
  • 2018-02-13
  • 2018-11-12
  • 1970-01-01
  • 2018-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多