今日のnodejsでasync / awaitを使用する

結局のところ、非同期呼び出しとスパゲッティコードのコールバックに誰もが長い間うんざりしていませんか? 幸いなことに、es6は、頭痛のない非同期機能を使用するための新しいasync / await sugarを導入しました。



典型的な問題を想像してください。 顧客は商品をバスケットに入れ、「支払い」をクリックしました。 バスケットはノードに飛び去り、新しい注文が作成され、クライアントは支払いシステム内の支払いシステムへのリンクを受け取ります。



アルゴリズムは次のようなものです。



  1. POSTメソッドを使用してクライアントから製品IDを取得します
  2. データベース内のこれらの商品の価格を見てください
  3. データベースに注文します
  4. 支払いシステムにPOSTリクエストを送信して、支払い用の一意のリンクを返すようにします
  5. すべてが問題なければ、このリンクをクライアントに提供します
  6. 邪悪なコメントを書く前に、理想的な世界に住んでおり、クライアント、データベース、リクエストにエラーがないことを想像する必要があります...面白くない


通常のコードは次のようになります。



const http = require('http'); const request = require('request'); const mysql = require('mysql') .createConnection() .connect() http.createServer( function(req, res){ //      res,         let clientRes = res; //   // ...    POST  //    id  : let order = { ids: [10,15,17], phone: '79631234567' } //    mysql.query('select cost from myshop where id in', order.ids, function(err, res){ //  res       .    ( ,      .          ) let totalPrice = 0; for(let i = 0; i < prices.result.length; i++){ totalPrice += prices.result[i].price; } //       (  ,     ) mysq.query('insert into orders set client=?, ids=?, status=1', [order.phone, order.ids.join(',')], function(err, res){ // mysql  insertId,          let insertId = res.insertId; request.post('http://api.payment.example.com', {form: { name: `  ${insertId}`, amount: totalPrice }}, function(err, res, body){ //  JSON    let link = JSON.parse(body).link; //    clientRes.end(link); //      ,       mysql.query('update orders set status=2 where id=?', insertId, function(err, res){ console.log(`  ${insertId} `); }); }); }); }); }).listen(8080);
      
      





コールバックはいくつカウントされましたか?



これまで、ノードの下の多くのライブラリーは、約束を与える方法を知らないため、さまざまなプロミスフィケーターを使用する必要があります。 しかし、promiseを使用してもasync / awaitほどすばらしいものではありません。



そして、このコードはどうですか?



 const http = require('http'); const request = require('request'); const promise = require('promise'); const mysql = require('mysql') .createConnection() .connect() http.createServer( async function(req, res){ //      res,         let clientRes = res; //   // ...    POST  //    id  : let order = { ids: [10,15,17], phone: '79631234567' } //   let selectCost = await promise(mysql, mysql.query, 'select cost from myshop where id in', order.ids); // ,     selectCost{ err: ..., res: ... } //   let totalPrice = 0; for(let i = 0; i < prices.result.length; i++){ totalPrice += prices.result[i].price; } //       let newOrder = await promise(mysql, mysql.query, 'insert into orders set client=?, ids=?, status=1', [order.phone, order.ids.join(',')]); let insertId = newOrder.res.insertId; //     //     let payment = await promise(request, request.post, {form: { name: `  ${insertId}`, amount: totalPrice }}); //         let link = JSON.parse(payment.res.body).link; //    clientRes.end(link); //    let updateOrder = await promise(mysql, mysql.query, 'update orders set status=2 where id=?', insertId); console.log(`  ${insertId} `); }).listen(8080);
      
      





単一のコールバックではありません、カール! phpのように見えます。 promise関数の内部で何が起こりますか? 魔法は非常に簡単です。 基本的に、すべての関数は2つのオブジェクトをコールバックに渡します:エラーと結果。 これは、関数の使用の普遍性になります。



 "use strict" function promise(context, func, ...params){ //    ,   (,  )         //     return new Promise( resolve => { // ,     func.call(context, ...params, (...callbackParams) => { //    ,     resolve,       (.  - promiseToAssoc); let returnObject = promiseToAssoc([...callbackParams]); resolve( returnedObject ); }) }) } /*         */ function promiseToAssoc(results){ let res = {}; //  3 ,         err, res  body let assoc = ['err', 'res', 'body']; for(let i = 0; i < results.length; i++){ //   (  )   field_3, field_4  . let field = assoc[i] || `field_${i}`; res[ field ] = results[i]; } return res; } module.exports = promise;
      
      





つまり、実際には、この関数でメソッドをラップすることにより、出力で次のようになります



 let result = await promise(fs, fs.readFile, 'index.html'); // result = {err: null, res: ' '}
      
      





そのようなこと。 コードの最後の部分をpromiseというファイルに押し込み、コールバックを削除したい場所でconst promise = require( './ promise');と記述します。



よりエレガントな使用方法があります。 そこに到達したいパラメーターの名前をスローできます。



たとえば、ファイルの存在をチェックするとき(fx.exist)、メソッドはtrueまたはfalseの1つの値のみを返します。 そして、現在のコードでは、if(fileExist.err)console.log( 'file found')を使用する必要がありますが、これは良くありません。 まあ、拒否にエラーを入れておくといいでしょう。



次のようなループで並列クエリを実行することもできます。



 var files = ['1.csv', '2.csv', '3.csv']; var results = []; //   for(let i = 0;i<files.length;i++){ results.push( promise(fs, fs.readFile, files[i]) ); } //     (   3   ,      .      ,     ) for(let i = 0;i<files.length;i++){ results[i] = await results[i]; }
      
      





このコードは、約6か月間、APIへの1秒あたり最大300リクエストの固定された負荷で運用中に使用されます(いいえ、これはこの例のオンラインストアではありません)(その時点で、非同期/待機サポート付きのバージョン8ノードがリリースされました)。 純粋な形式の同じコールバックまたは約束と比較して、バグ、リーク、またはパフォーマンスの低下はありませんでした。 時計のように。 さらに、各メソッドは、0〜10個のそのような待機を生成します。



この記事はノーベル賞であるとは主張しておらず、そのようなコードはgithubに置くのが怖いです。 頭は松葉杖/ govnokod /自転車の石の準備ができていますが、それにもかかわらず、誰かがこの使用のアイデアを面白いと思うかもしれません。



async / awaitの使い方をコメントで書いてください。



All Articles