Node.js / Express.js无法通过Request中间件从API调用中获取身体响应

时间:2016-10-31 14:28:49

标签: javascript node.js express request

我试图通过Express.js框架学习Node.js。目前,我需要进行API调用以获取一些对我的应用程序有用的数据。

API调用是使用Request middleware进行的,但当我退出请求时,我的变量将变为未定义...让我告诉你:

var request = require('request');
var apiKey = "FOOFOO-FOOFOO-FOO-FOO-FOOFOO-FOOFOO";
var characters = [];
var gw2data;
var i = 0;

module.exports.account = function() {
    request('https://api.guildwars2.com/v2/characters/?access_token=' + apiKey, function (error, response, body) {
        gw2data = JSON.parse(body);
        console.log('out request ' + gw2data); // {name1, name2 ...}
        for (i; i < gw2data.length; i++) {
            getCharacInfo(gw2data[i], i);
        }
    });
    console.log('out request ' + characters); // undefined
    return characters;
};

function getCharacInfo (name, position) {
    request('https://api.guildwars2.com/v2/characters/' + name + '/?access_token=' + apiKey, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            characters[position] = JSON.parse(body);
        }
    });
}

我不明白为什么当我退出请求时gw2data变量变得不确定...有人可以解释一下吗?

编辑:我来找你,因为我的问题已经改变了,我现在需要在我的第一个API调用中进行API调用循环,我想是同样的异步问题。

之前的代码已根据之前的答案进行了演变:

module.exports.account = function(cb) {
    var request = require('request');
    var apiKey = "FOOFOO-FOOFOO-FOO-FOO-FOOFOO-FOOFOO";
    var characters = [];
    var i = 0;
    request('https://api.guildwars2.com/v2/characters/?access_token=' + apiKey, function(err, res, body) {
        var numCompletedCalls = 1;
        for (i; i < JSON.parse(body).length; i++) {
            if (numCompletedCalls == JSON.parse(body).length) {
                try {
                    console.log(characters); // {} empty array
                    return cb(null, characters);
                } catch (e) {
                    return cb(e);
                }
            }else {
                getCharacInfo(JSON.parse(body)[i]);
            }
            numCompletedCalls++;
        }
    });
};

function getCharacInfo (name) {
    request('https://api.guildwars2.com/v2/characters/' + name + '/?access_token=' + apiKey, function (err, res, body) {
        if (!err) {
            console.log(characters); // {data ...}
            characters.push(JSON.parse(body));
        }
    });
}

这个函数的调用:

var express = require('express');
var router = express.Router();

router.get('/', function(req, res, next) {
    var charactersInfos = require('../models/account');
    charactersInfos.account(function(err, gw2data) {
        if (err) {
            console.error('request failed:', err.message);
        } else {
            res.render('index', {
                persos : gw2data
            });
        }
    });
});

问题是,当我返回字符数组时,它总是一个空数组,但当我检查我的函数getCharacInfo时,数组包含数据......

2 个答案:

答案 0 :(得分:1)

第二个gw2dataundefinedconsole.log的原因是因为您过早登录。 request异步操作,因此它会立即返回 ...但是,这并不代表回调。

所以基本上你在实际设置之前记录gw2data 的做法是什么。在处理异步操作时,最好的方法是通常使自己的方法也是异步的,这可以通过几种方式实现 - 最简单的方法就是让你的函数接受一个以异步方式期望数据的回调,例如。

module.exports.account = function(cb) {
    request(..., function(err, res, body) {
        // return an error if `request` fails
        if (err) return cb(err);
        try {
            return cb(null, JSON.parse(body));
        } catch (e) {
            // return an error if `JSON.parse` fails
            return cb(e);
        }
    });
}
...
var myModule = require('mymodule');
myModule.account(function(err, g2wdata) {
  if (err) {
    console.error('request failed', err.message);
  } else {
    console.log('out request', g2wdata);  
  }
});

根据您的语法,我假设您不使用ES6,值得一看(特别是如果您刚刚开始学习)。内置承诺&amp; async-await支持相对很快这种东西变得相对简单(至少在主叫端),例如。

export default class MyModule {
    account() {
      // return a promise to the caller
      return new Promise((resolve, reject) => {
        // invoke request the same way but this time resolve/reject the promise
        request(..., function(err, res, body) {
          if (err) return reject(err);
          try {
            return resolve(JSON.parse(body));
          } catch (e) {
            return reject(e);
          }
        }); 
      });
    }
}
...
import myModule from 'mymodule';

// handle using promises
myModule.account()
  .then(gw2data => console.log('out request', gw2data))
  .catch(e => console.error('request failed', e));

// handle using ES7 async/await
// NOTE - self-executing function wrapper required for async support if using at top level,
//        if using inside another function you can just mark that function as async instead
(async () => {
  try {
    const gw2data = await myModule.account();
    console.log('out request', gw2data);
  } catch (e) {
    console.log('request failed', e);
  }
})();

最后,如果您决定沿着Promise路线前进,那么有几个库可以将Promise支持填充到现有库中。我能想到的一个例子是Bluebird,根据我所知道的经验与request一起工作。将其与ES6相结合,您将获得非常简洁的开发人员体验。

答案 1 :(得分:0)

请求API是异步的,因为Node.js中的所有I / O都是异步的。查看Promise API是IMO编写异步代码的最简单方法。有一些Promise适配器可用于请求,或者您可以使用superagent之类的promise支持。