web-dev-qa-db-ja.com

Javascript:プロミスチェーンvs非同期/待機?

Javascript Promiseおよびasync/awaitについて学習しています。以下のサンプルコードは、node.js内のJSONファイルを非同期で読み取り、解析します(my node.js version is v10.0.0)。

サンプルコードでは、ChainReadJson関数とAwaitReadJson関数が同じことを行って、JSONファイルの読み取りと解析を行っています。違いは、ChainReadJson関数がpromiseチェーンを使用するのに対し、AwaitReadJson関数はasync/awaitを使用することです。

const FS = require("fs");

function ReadFile(fileName) {
    return new Promise((Resolve, Reject) => {
        FS.readFile(fileName, 'utf8', (error, result) => {
            if (error)
                Reject(error);
            else
                Resolve(result);
        });
    });
}

// function using promise chain

function ChainReadJson(fileName, CallBack) {
    ReadFile(fileName)
        .then(
            res => JSON.parse(res),
            err => {
                Message(-1, err.message);
            }
        )
        .then(
            res => {
                if (res !== undefined)
                    CallBack(fileName, res);
            },
            err => {
                Message(-2, err.message);
            }
        );
}

// function using async/await

async function AwaitReadJson(fileName, CallBack) {
    let res, json;

    try {
        res = await ReadFile(fileName);
    }
    catch (err) {
        Message(-1, err.message);
        return;
    }
    try {
        json = JSON.parse(res);
    }
    catch (err) {
        Message(-2, err.message);
        return;
    }
    CallBack(fileName, json);
}

ChainReadJson('test.json', PrintJSON);
AwaitReadJson('test.json', PrintJSON);

// common functions

function PrintJSON(fileName, json) {
    console.log(`JSON[${fileName}]:`, json);
}

function Message(n, str) {
    console.log(`[${n}]`, str);
}

プロミスチェーンを使用してChainReadJson関数のコードを記述するとき、実行結果とエラーの制御が困難でした。ただし、async/awaitを使用してAwaitReadJson関数のコードを作成すると、これらの問題はほとんどなくなります。

Async/awaitの利点を正しく理解していますか?プロミスチェーンと比較した非同期/待機のデメリットは何ですか?

(サンプルコードは この回答のコード の変更バージョンです。元のコードはプロミスチェーンのみを使用し、チェーン内のどこでエラーが発生し、何がエラーであるかを正確に知るように記述されています)

11
pdh0710

確かに、 async/await was designed は、定型文を減らし、非同期プログラムを、コールバック、プロミス、ジェネレーター関数に比べて簡単に作成できるようにします。

  • プロミスは同じ目標で作成されましたが、既存のJSエンジンで動作する必要があるという追加の制約がありました。そのため、構文はより複雑です。 async/awaitを使用するには 比較的新しいJSエンジンが必要です。独自のnode.jsアプリを作成しているかどうかは問題ではないかもしれませんが、ライブラリは古いnode.jsバージョンと互換性があります(ジェネレーターのサポートなしで古いブラウザーで使用するためにそれをトランスパイルできるかどうかはわかりません)。
  • Async/awaitの方が新しいため、最適化されていない。昨年行われた比較 reports Bluebird promise(簡素化されたバージョンのpromiseを実装するJSライブラリ)は、特定のベンチマークでasync/awaitよりも優れています。 (もちろん、ユースケースがいくつかのネットワーク要求を行っている場合、これは問題にならない場合があります。)
  • あなたはまだ いくつかの非同期アクションを並行して実行するという約束が必要 (編集:結果が必要な場合)
9
Nickolay

async/awaitは非同期ロジックをクリーンアップするための優れた方法です。promiseロジックを大幅にクリーンアップして、非同期/待機の代替手段に非常に類似していることを指摘しておくことは価値があります。

const fs = require("fs");
const util = require("util")

//Could also use the experimental "fs/promise" api from node v10
const promisifiedReadFile = util.promisify(fs.readFile);

const readFile = (fileName) => promisifiedReadFile(fileName, 'utf8');

function chainReadJson(fileName, callback) {
    return readFile(fileName)
        .then(json => JSON.parse(json))
        .then(result => callback(null, result))
        .catch(e => {
            console.log("Error reading or parsing file", e.message);
            callback(e)
        });
}

ここでの唯一の機能的な違いは、すべてのエラーログがチェーンの最後の1か所で発生することです。

ReadFileとJSON.parseのスプリットロギングを保持することは可能ですが、それは確かに少しトリッキーです。エラーを処理した後、通常、エラーを再スローして、ダウンストリーム.thenハンドラーはスキップされます。ただし、エラーを再度スローすると、ダウンストリームによって再度キャッチされます.catchハンドラ。これを除外する方法が見つからない場合、重複ロギングが発生します。

実行可能ですが、少し面倒なので、上のコードでは省略しました。

3
Retsam