如何在回调函数中访问app.get的'response'参数

时间:2017-09-17 13:19:54

标签: javascript node.js google-api

我想将我通过google drive API获取的文件列表(const char* host = "http://localhost/mysql0.php?value"+ value; )传递给EJS文件。

即。我想写

obj

问题是我通过一些回调函数获取了js对象。 此功能称为

app.get('/',function(req,res){
  res.render('index',obj);
}

反过来调用,

fs.readFile('client_secret.json',processClientSecrets );

调用这两个,

function processClientSecrets(err,content) {
if (err) {
  console.log('Error loading client secret file: ' + err);
  return;
}else{
  authorize(JSON.parse(content),findFiles);
 }
}

[编辑]

function authorise(credentials,callback) {
var clientSecret = credentials.installed.client_secret;
  var clientId = credentials.installed.client_id;
  var redirectUrl = credentials.installed.redirect_uris[0];
  var auth = new googleAuth();
  var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);

  // Check if we have previously stored a token.
  fs.readFile(TOKEN_PATH, function(err, token) {
    if (err) {
      getNewToken(oauth2Client, callback);
    } else {
      oauth2Client.credentials = JSON.parse(token);
      callback(oauth2Client);
    }
  });
}

这看起来像一个非常基本的问题,但我无法解决它,因为node.js本质上是异步的,我所有尝试返回obj都导致在检索它之前渲染obj。

1 个答案:

答案 0 :(得分:1)

欢迎回调地狱。 :-)旧的“节点”方式是做嵌套回调,这很快变得非常丑陋。

现代方法是使用promises,这样可以更容易地组合多个异步操作。让你自己的异步函数返回promises,对于Node API函数(或者还没有提供promises的附加库),使用包装器使它们能够启用promise(手动或使用promisify之类的东西)

例如,使用基于承诺的函数,您的调用将如下所示:

app.get('/',function(req,res){
    readFilePromise('client_secret.json')
        .then(content => JSON.parse(content))
        .then(authorise)
        .then(findFiles)
        .then(files => {
            res.render('index', files);
        })
        .catch(err => {
            // Render error here
        });
});

或由于JSON.parsefindFiles都不是异步的:

app.get('/',function(req,res){
    readFilePromise('client_secret.json')
        .then(content => authorise(JSON.parse(content)))
        .then(auth => {
            res.render('index', findFiles(auth));
        })
        .catch(err => {
            // Render error here
        });
});

使用then的非异步函数是很好的,只要函数需要一个参数并返回处理结果,所以第一个版本也没问题,尽管有一个涉及开销。

在这两种情况下,readFilePromise都是readFile的宣传版,authorize看起来像

function authorise(credentials) {
    var clientSecret = credentials.installed.client_secret;
    var clientId = credentials.installed.client_id;
    var redirectUrl = credentials.installed.redirect_uris[0];
    var auth = new googleAuth();
    var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);

    // Check if we have previously stored a token.
    return readFilePromise(TOKEN_PATH)
        .then(token => {
            oauth2Client.credentials = JSON.parse(token);
            return oauth2Client;
        });
}

(另请注意 - 主观性警告! - 因为我们最终没有地狱般的深层嵌套回调结构,我们可以使用合理的缩进宽度而不是两个空间这么多Node程序员认为需要采纳。)

继续前进,如果您使用的是Node V8.x +,则可以使用async / await语法来使用这些承诺:

app.get('/', async function(req, res){
    try {
        const credentials = JSON.parse(await readFilePromise('client_secret.json'));
        const auth = await authorize(credentials);
        const files = findFiles(auth);
        res.render('index', files);
    } catch (e) {
        // Render error here
    }
});

注意async之前的function以及await我们在调用返回承诺的函数时的任何时间。 async函数返回一个承诺,await消费承诺。代码看起来是同步,但不是。每个await实际上是对then的调用,以便在promise完成时注册回调。同样,try / catch实际上是对promise链上catch方法的调用。

如果我们想要的话,我们可以强调:

app.get('/', async function(req, res){
    try {
        res.render('index', findFiles(await authorize(JSON.parse(await readFilePromise('client_secret.json'))));
    } catch (e) {
        // Render error here
    }
});

...但可读性/可调试性受到影响。 : - )

重要提示:将async函数传递给某些事物(如app.get)并且不希望该函数返回一个承诺时,必须将其包装在一个承诺中如上所述try / catch并处理任何错误,因为如果调用代码不期望承诺,它将不会处理承诺拒绝,您需要这样做;未处理的拒绝是一件坏事(在未来的Node版本中会导致您的进程终止)。

如果你将async函数传递给 期望函数返回一个进程,最好关闭try/ catch`并允许传播错误。

您向findFiles寻求帮助。我建议学习promisify或类似的东西。解决这个问题的正确方法(在我看来)就是给自己一个drive.files.list的宣传版本,因为drive.files.list使用了Node风格的回调。

但如果没有宣传,我们可以这样做:

function findFiles(auth) {
    var drive = google.drive('v3');
    return new Promise(function(resolve, reject) {
        drive.files.list({
            auth: auth,
            folderId: '****************',
            q: "mimeType contains 'application/pdf' and trashed = false"
        },
        function(err, response) {
            if (err) {
                reject(err);
                return;
            }
            var f = response.files;
            if (f.length == 0) {
                console.log('No files found.');
            }
            else {
                var key = 'files'; // Why this indirection??
                resolve({[key]: f.map(file => file.name + ' ' + file.id)});
                // Without the indirection it would be:
                // resolve({files: f.map(file => file.name + ' ' + file.id)});
            }
        });
    });
}

如果我们有一个promisified版本,我们取消了似乎没必要的key间接,它会更简单:

function findFiles(auth) {
    return drivePromisified.files.list({
        auth: auth,
        folderId: '****************',
        q: "mimeType contains 'application/pdf' and trashed = false"
    }).then(files => ({files: files.map(file => file.name + ' ' + file.id)}));
}

或使用async作为await函数:

async function findFiles(auth) {
    const files = await drivePromisified.files.list({
        auth: auth,
        folderId: '****************',
        q: "mimeType contains 'application/pdf' and trashed = false"
    });
    return {files: files.map(file => file.name + ' ' + file.id)};
}