2017-09-17 73 views
1

我想将通过谷歌驱动API获取的文件列表(obj)传递给EJS文件。如何在回调函数中访问app.get的'response'参数

即我想写

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之前渲染它。

+0

控件到达readFile然后进入授权(我可以记录授权返回的对象),但在此之后它会捕获错误。 :( – AlwaysHungrie

回答

1

欢迎来到回拨地狱。 :-)旧的“节点”方式是做嵌套的回调,很快就会变得非常难看。

现代的方法是使用承诺,这使得更容易组合多个异步操作。使自己的异步函数返回承诺,并为节点API函数(或不提供承诺的附加库),使用包装使它们承诺启用(手动或使用类似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.parse也不findFiles是异步的:

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提供的功能需要一个参数并返回处理结果,所以第一个版本也很好,虽然有以上涉及头部。

在这两种情况下,readFilePromisereadFile一个promisified版本,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; 
     }); 
} 

(还要注意  —主观性警告  —因为我们不结束我们可以使用合理的缩进宽度而不是两个空格,因此许多节点程序员认为需要采用这两个空格。)

继续前进,如果您使用的是节点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 
    } 
}); 

functionasyncawait任何时候我们调用返回一个承诺的功能。 async函数返回一个承诺下的承诺,并且await承担下承诺。代码看起来同步,但不是。每个await实际上是一个呼叫then注册一个回调,当承诺完成。同样,try/catch实际上是对承诺链上的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的promised版本,因为drive.files.list改为使用Node风格的回调。

但是,如果没有promisifying它,我们可以这样做:

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

或者用awaitasync功能:

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

先生谢谢你的回答,实际上我的'findFile'函数也是异步的,你是否会善意地帮我promisify呢, – AlwaysHungrie

+0

@AlwaysHungrie:Tricky,因为你还没有显示它的内容。:-)但无论如何,这对读者来说是最好的锻炼:只要它能够回报一个承诺。例如,如果使用['readdir'](https://nodejs.org/api/fs.html#fs_fs_readdir_path_options_callback),你会像上面的'readFile'一样让步,并从中返回承诺(可能与如果你需要转换结果,那么就会链接它)。 –

+0

对不起,我编辑的问题包括'findFile',我试着返回drive.files。但是代码只是进入catch(e)块。 – AlwaysHungrie

相关问题