我想将我通过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。
答案 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.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
的宣传版,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)};
}