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

标签 javascript node.js google-api

我想将我通过 google drive API 获取的文件列表 (obj) 传递给 EJS 文件。

即我要写

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

问题是我通过一些回调函数获取了 js 对象。 这个函数叫做

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);
    }
  });
}

[编辑]

function findFiles(auth){
var obj ={};
var key = 'files';
obj[key]=[];
var drive = google.drive('v3');
drive.files.list({
                auth: auth,
                folderId: '****************',
                q: "mimeType contains 'application/pdf' and trashed = false"
                },
  function(err,response){
    var f = response.files;
    if (f.length == 0) {
    console.log('No files found.');
    }else {
        var i;
        for (i = 0; i < f.length; i++) {
        var file = f[i];
        //console.log('%s (%s)', file.name, file.id);
        obj[key].push(file.name + ' ' + file.id);
        }
        console.log(obj);
        return obj;
  }
                  });

}

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

最佳答案

欢迎来到回调 hell 。 :-) 旧的“Node ”方式是进行嵌套回调,这很快就会变得非常丑陋。

现代方法是使用 promises,这使得将多个异步操作组合在一起变得更加容易。让你自己的异步函数返回 promise ,对于 Node API 函数(或尚未提供 promise 的附加库),使用包装器使它们启用 promise (手动,或使用类似 promisify 的东西)。

例如,对于基于 promise 的函数,您的调用将如下所示:

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 的 promise 版本,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;
        });
}

(另请注意——主观警告!——因为我们不会以 hell 般的深层嵌套回调结构结束,我们可以使用合理的缩进宽度而不是两个空格,因此许多 Node 程序员认为需要采用。)

更进一步,如果您使用的是 Node V8.x+,则可以使用 async/await 语法来使用这些 promises:

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
    }
});

注意 function 之前的 asyncawait 任何时候我们调用一个返回 promise 的函数。 async 函数在幕后返回一个 promise,而 await 在幕后使用 promise。代码看起来 是同步的,但实际上不是。每个 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 函数传递给不期望该函数返回 promise 的对象(如 app.get)时,您必须 将其包装在 try/catch 中并处理任何错误,因为如果调用代码不期望 promise ,它将不会处理 promise 拒绝,你需要这样做;未处理的拒绝是一件坏事(在未来的 Node 版本中将导致您的进程终止)。

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


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

但无需 promise ,我们可以这样做:

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)}));
}

或者使用 await 作为 async 函数:

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)};
}

关于javascript - 如何在回调函数中访问app.get的 'response'参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46264516/

相关文章:

javascript - 使用 JavaScript 创建在线 CMD/终端

javascript - 如何在 Polymer 中获取查询字符串

Android 将 CID 位置转换为坐标

flutter - 如何根据 Gmail 或 Facebook 登录显示个人资料图片?

javascript - 我的谷歌图片搜索中没有图片

javascript - HTML5音频点击进度条移动到不同时间

javascript - 如何在评分表中显示姓名

javascript - 如何在 Sails.js 上向本地添加配置设置

node.js - 如何在Azure Functions node.js中获取客户端IP地址?

javascript - 清理 Javascript/Nodejs 代码