2017-09-17 6 views
1

GoogleドライブAPIを使用してEJSファイルに渡しているファイルのリスト(obj)を渡したいとします。コールバック関数でapp.getの 'response'パラメータにアクセスする方法

つまり私は

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

に問題を書きたいが、私はいくつかのコールバック関数を使ってjsオブジェクトを取得していますということです。 この関数は、

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

、ターンの呼び出しでこれらの二つの呼び出しを

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

と呼ばれる

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

[EDIT]

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に達し、次にAuthorizeに行きます(私はauthorizeによって返されたオブジェクトをログに記録できますが、それ以降はエラーをキャッチします)。 : – AlwaysHungrie

答えて

1

ようこそコールバック地獄です。 :-)古い "Node"のやり方はネストされたコールバックを行うことであり、非常に醜いです。

現代的なアプローチは、複数の非同期操作を一緒に構成することを容易にする約束を使用することです。独自の非同期関数が約束を返すようにし、ノード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.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であれば、関数は1つのパラメータを期待して処理された結果を返すので、最初のバージョンも正常です。ビット頭がかかわった。 (また  —主観性の警告に注意し  —ことを、我々は終わっていないので、

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

:!両方のケースで

readFilePromisereadFileのpromisifiedバージョンで、authorizeはこのような何かに見えます地獄の深くネストされたコールバック構造では、2つのスペースではなく合理的なインデント幅を使用できるため、多くのNodeプログラマが採用する必要があると感じていました)

さらにノードV8を使用している場合はさらに進みます。

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

は注意functionawaitasync私たちは約束を返す関数を呼び出している任意の時間を:X +、あなたはそれらの約束を消費する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 
    } 
}); 

...しかし、読みやすさ/ debuggabilityが苦しみます。 :-)

重要な注意:約束を返すために機能を期待されていないこと(app.getのような)何かにasync機能を渡すとき、あなた上記のようtry/catchでそれをラップし、すべてのエラーを処理する必要がありますなぜなら、呼び出しコードが約束を期待していなければ、それは約束の拒否を処理しないので、それを行う必要があるからです。未処理の拒否は悪いことです(そして、Nodeの将来のバージョンでは、プロセスが終了することになります)。

あなたはasync機能を渡しているものならばtry/ catch`をオフのままにしてエラーが伝播することを可能にするために、プロセスを返す関数を期待する最高ありません。


あなたはfindFilesのヘルプを求めました。私はpromisifyかそれと似たようなことを学ぶことをお勧めします。これを解決する正しい方法は、drive.files.listという有名なバージョンを提供することです。drive.files.listは代わりにノードスタイルのコールバックを使用するためです。

しかし、それを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)})); 
} 

それとも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)}; 
} 
+0

あなたの答えをありがとう、実際には私の 'findFile'関数も非同期です、私もそれを約束するのに十分親切でしょうか – AlwaysHungrie

+0

@AlwaysHungrie:あなたのコンテンツを表示していないのでトリッキーです。:-)しかし、とにかく読者のためのエクササイズとしては最高です。単に約束を返すようにしてください。たとえば、[readdir'](https://nodejs.org/api/fs.html#fs_fs_readdir_path_options_callback)を使用すると、上記の 'readFile'と同様に、そのことを約束することができます。結果を変換する必要がある場合は、[then]をクリックします)。 –

+0

申し訳ありませんが、私は 'findFile'を含むように質問を編集しました。私はdrive.filesを返そうとしました。ただし、コードはcatch(e)ブロックに移動します。 – AlwaysHungrie

関連する問題