web-dev-qa-db-ja.com

トップレベルでasync / awaitを使うにはどうすればいいですか?

私は非同期/待機を続けてきました、そしていくつかの記事を調べた後、私は自分自身でテストすることにしました。しかし、これがうまくいかないのは、頭を包むようには思えません。

async function main() {  
    var value = await Promise.resolve('Hey there');
    console.log('inside: ' + value);
    return value;
}

var text = main();  
console.log('outside: ' + text)

コンソールは次のように出力します(ノードv8.6.0)。

> outside:[オブジェクトの約束]

> inside:ちょっとそこ

関数内のログメッセージが後で実行されるのはなぜですか。 async/awaitが作成された理由は、非同期タスクを使用して同期実行を実行するためであると思いました。

.then()の後にmain()を使わずに関数内で返された値を使う方法はありますか?

81
Felipe

これがうまくいかないのは、頭を包むようには思えない。

なぜならmainは約束を返すからです。すべてのasync関数が行います。

最上位レベルでは、次のいずれかを行う必要があります。

  1. 拒否しないような最上位のasync関数を使用します( "未処理の拒否"エラーが発生しない限り)。

  2. thenおよびcatchを使用する

  3. (近日公開予定)最上位レベルのawait を使用します。これは、ステージ3に到達したという提案です。 process では、モジュール内でawaitをトップレベルで使用できます。

#1 - 決して拒否されないトップレベルのasync関数

(async () => {
    try {
        var text = await main();
        console.log(text);
    } catch (e) {
        // Deal with the fact the chain failed
    }
})();

catchに注意してください。あなたは約束の拒否/非同期例外を扱わなければならない。あなたに渡すための呼び出し元はいません。お望みであれば、(catch/try構文ではなく)catch関数を介してそれを呼び出した結果でそれを行うことができます。

(async () => {
    var text = await main();
    console.log(text);
})().catch(e => {
    // Deal with the fact the chain failed
});

...これはもう少し簡潔です(私はその理由でそれが好きです)。

または、もちろん、エラーを処理せず、単に「未処理の拒否」エラーを許可します。

#2 - thencatch

main()
    .then(text => {
        console.log(text);
    })
    .catch(err => {
        // Deal with the fact the chain failed
    });

チェーン内またはcatchハンドラ内でエラーが発生した場合は、thenハンドラが呼び出されます。 (catchハンドラはエラーを処理するためのものが何も登録されていないため、エラーをスローしないようにしてください。)

あるいはthenへの両方の引数:

main().then(
    text => {
        console.log(text);
    },
    err => {
        // Deal with the fact the chain failed
    }
);

再度、拒否ハンドラを登録しています。しかしこの形式では、あなたのthenコールバックのいずれもエラーをスローしないこと、それらを処理するための登録がないことを確認してください。

モジュールの#3最上位await

非モジュールスクリプトのトップレベルでawaitを使用することはできませんが、トップレベルのawaitプロポーザルでは、モジュールのトップレベルでそれを使用できます。トップレベルのコードを拒否(エラーをスロー)させたくないという点で、トップレベルのasync関数ラッパー(上記の#1)を使用するのと同じことが、未処理の拒否エラーが発生するためです。そのため、#1のように、問題が発生したときに未処理の拒否を行わないようにするには、コードをエラーハンドラでラップする必要があります。

// In a module, once the top-level `await` proposal lands
try {
    var text = await main();
    console.log(text);
} catch (e) {
    // Deal with the fact the chain failed
}
113
T.J. Crowder

トップレベルawait がステージ3に移動したため、質問への回答トップレベルでasync/awaitを使用するにはどうすればよいですか?awaitへの呼び出しをmain()に追加するだけです:

async function main() {  
    var value = await Promise.resolve('Hey there');
    console.log('inside: ' + value);
    return value;
}

var text = await main();  
console.log('outside: ' + text)

あるいは単に:

const text = await Promise.resolve('Hey there');
console.log('outside: ' + text)

[email protected] でのみ利用可能であることに注意してください。

2
lautaro.dragan

この問題に対する実際の解決策は、それとは異なる方法で対処することです。

おそらくあなたの目標は、通常アプリケーションのトップレベルで行われるある種の初期化です。

解決策は、アプリケーションのトップレベルにJavaScriptステートメントが1つだけ存在するようにすることです。アプリケーションの先頭に文が1つしかない場合は、どこにでもasync/awaitを自由に使用できます(もちろん通常の構文規則による)。

別の言い方をすると、最上位レベルがもう最上位レベルではなくなり、アプリケーションの最上位レベルでasync/waitを実行する方法の問題を解決するように、最上位レベル全体を関数でラップするということです。

これがあなたのアプリケーションのトップレベルがどのように見えるべきかということです:

import {application} from './server'

application();
2
Duke Dougal

現在の答えの上にいくつかのさらなる情報を与えるために:

node.jsファイルの内容は、現在、文字列のように連結されて関数本体を形成しています。

たとえばtest.jsというファイルがあるとします。

// Amazing test file!
console.log('Test!');

node.jsは密かに以下のような関数を連結します。

function(require, __dirname, ... a bunch more top-level properties) {
  // Amazing test file!
  console.log('test!');
}

注意すべき重要な点は、結果として得られる関数は非同期関数ではないということです。そのため、awaitという用語を直接その中に使用することはできません。

しかし、このファイルでプロミスを扱う必要があるとしたら、2つの方法があります。

  1. 関数内でawait直接を使用しない
  2. awaitを使用しない

オプション1では、新しいスコープを作成する必要があります(これを制御できるので、このスコープをasyncにすることができます)。

// Amazing test file!
// Create a new async function (a new scope) and immediately call it!
(async () => {
  await new Promise(...);
  console.log('Test!');
})();

オプション2では、オブジェクト指向のpromise APIを使用する必要があります(promiseを扱う際の、あまり機能的ではありませんが、機能的なパラダイム)

// Amazing test file!
// Create some sort of promise...
let myPromise = new Promise(...);

// Now use the object-oriented API
myPromise.then(() => console.log('Test!'));

実行可能であれば、node.jsがデフォルトでコードをasync関数に連結することを私は個人的に願っています。それはこの頭痛を取り除きます。

1
Gershom Maes

main()は非同期的に実行されるので、約束を返します。あなたはthen()メソッドで結果を得なければなりません。 then()もpromiseを返すので、プログラムを終了するにはprocess.exit()を呼び出さなければなりません。

main()
   .then(
      (text) => { console.log('outside: ' + text) },
      (err)  => { console.log(err) }
   )
   .then(() => { process.exit() } )
0
Peracek