如何在回调函数中访问app.get的'response'参数
我想将通过谷歌驱动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之前渲染它。
欢迎来到回拨地狱。 :-)旧的“节点”方式是做嵌套的回调,很快就会变得非常难看。
现代的方法是使用承诺,这使得更容易组合多个异步操作。使自己的异步函数返回承诺,并为节点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
提供的功能需要一个参数并返回处理结果,所以第一个版本也很好,虽然有位以上涉及头部。
在这两种情况下,readFilePromise
是readFile
一个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
}
});
注function
前async
和await
任何时候我们调用返回一个承诺的功能。 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)}));
}
或者用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)};
}
先生谢谢你的回答,实际上我的'findFile'函数也是异步的,你是否会善意地帮我promisify呢, – AlwaysHungrie
@AlwaysHungrie:Tricky,因为你还没有显示它的内容。:-)但无论如何,这对读者来说是最好的锻炼:只要它能够回报一个承诺。例如,如果使用['readdir'](https://nodejs.org/api/fs.html#fs_fs_readdir_path_options_callback),你会像上面的'readFile'一样让步,并从中返回承诺(可能与如果你需要转换结果,那么就会链接它)。 –
对不起,我编辑的问题包括'findFile',我试着返回drive.files。但是代码只是进入catch(e)块。 – AlwaysHungrie
控件到达readFile然后进入授权(我可以记录授权返回的对象),但在此之后它会捕获错误。 :( – AlwaysHungrie