最近、私はphantomjs-node
ライブラリを試しています。私が達成したいことは、基本的に動的なWebページテンプレートを作成し、phantomjs-node
ライブラリを使用して "実行"し、最後にレンダリングされたページからデータを抽出することでした。Node.js&co - 約束とイベントコールバックの混同を避ける
page.evaluate
によって返された約束の
then
-callbackが最終的に呼び出されたときに、メインファントム・プロセスがすでに持っているので
12/18/2017, 2:44:32 PM - info: start
12/18/2017, 2:44:33 PM - info: done
12/18/2017, 2:44:33 PM - info: onLoadFinished
12/18/2017, 2:44:33 PM - error: Phantom process stopped with exit code 0
最も可能性が高い:
var phantom = require('phantom');
var co = require('co');
var sleep = require('system-sleep');
var winston = require('winston');
const logger = new winston.Logger({
level: 'debug',
transports: [new winston.transports.Console({
json: false, timestamp:() => (new Date()).toLocaleString()
})]
});
co(function*() {
logger.info('start');
var instance = yield phantom.create();
try {
const html = `
<!DOCTYPE html>
<html>
<head>
<title>Page title</title>
</head>
<body>
<div id='results'>Page data</div>
</body>
</html>
`;
var page = yield instance.createPage();
yield page.on('onLoadFinished', function(){
logger.info('onLoadFinished');
page.evaluate(function(){
return document.getElementById('results').textContent;
}).then(function(val){
logger.info(`RESULT = ${val}`);
}).catch(function(val){
logger.error(val.message);
});
});
yield page.setContent(html, 'http://localhost');
}catch (e){
logger.error(e.message);
}finally{
instance.exit();
}
logger.info('done');
});
しかし、これは出力で失敗します終了しました。
これを「修正」するためには、私は(下記の例の残りの部分を省略)は、次の即興戦略に頼っ:
var page = yield instance.createPage();
var resolver;
var P = new Promise(function(resolve, reject){ resolver = resolve; });
yield page.on('onLoadFinished', function(){
logger.info('onLoadFinished');
resolver(page.evaluate(function(){
return document.getElementById('results').textContent;
}));
});
yield page.setContent(html, 'http://localhost');
const val = yield P;
logger.info(`RESULT = ${val}`);
これは、本質的に「外部」で解決された新しい約束を作成するには約束はpage.evaluate
から返されました。 co
ブロックその後、ブロックの終わりにyield P
文で必要な結果が準備できるまで、期待通りので、出力は次のとおりです。
12/18/2017, 2:53:47 PM - info: start
12/18/2017, 2:53:48 PM - info: onLoadFinished
12/18/2017, 2:53:48 PM - info: RESULT = .....
12/18/2017, 2:53:48 PM - info: done
これが動作しているようですが、例えば、例外がスローされ、それは(かなり「ハック」と感じますコールバックではresolver
の呼び出しがメインのtry/catch
ブロックで検出されないため)コールバックからco
で管理されているコールバックに制御を「転送」するには、よりクリーンなアプローチが必要なのでしょうか?
詳細なコメントをありがとう! 'Promise'コンストラクタ内に別の' async'関数をネストする必要がありますか?私が理解する限り、 'page.on'呼び出しは非同期です(原則的に別の約束を返します)。もし実際には' page.setContent'が 'loadFinished'コールバックが実行される前に実行され、終了します。セット? – ewcz
@ewcz No. ['Promise'コンストラクタに' async関数 'を渡さないでください。](https://stackoverflow.com/a/43083793/1048572)! 'page.on'は約束を返すようではないので、それを(それを約束するために)ラップしているのです。また、* callback *は非同期です。つまり、 'setContent' call(https://stackoverflow.com/a/31099819/1048572)の後に実行されます(これは、それが原因です。前)。 – Bergi
私は、コンテンツが設定された状態でコールバック自体が開始されたことを理解しています。私は[source](https:// github)を理解する限り、 'page.on'を介してコールバックを割り当てる/基本的に[実行](https://github.com/amir20/phantomjs-node/blob/master/src/phantom.js)を呼び出しています。 )関数は約束を返します( 'page.on'の戻り値をコンソールに出力した場合、' Promise {} ') –
ewcz