web-dev-qa-db-ja.com

非同期待機でコールバックをラップするにはどうすればよいですか?

私の機能は、httpサーバーが起動するとすぐに解決する約束を解決します。これは私のコードです:

function start() {
    return new Promise((resolve, reject) {
        this.server = Http.createServer(app);
        this.server.listen(port, () => {
            resolve();
        });
    })
}

開始機能を非同期/待機に変換するにはどうすればよいですか?

11
Tiago Bértolo

関数宣言の前にasyncを、awaitコンストラクターにPromiseを含めます。ただし、本質的には既存のパターンにコードを追加することになります。 awaitは値をPromiseに変換しますが、質問のコードは既にPromiseコンストラクターを使用しています。

async function start() {
    let promise = await new Promise((resolve, reject) => {
        this.server = Http.createServer(app);
        this.server.listen(port, () => {
            resolve();
        });
    })
    .catch(err => {throw err});

    return promise
}

start()
.then(data => console.log(data))
.catch(err => console.error(err));
7
guest271314

この場合、他の回答が示唆するように_new Promise_を作成するとうまくいきますが、一般的な規則として til.promisify は同じことを何度も書くのを止めることができます。

そのため、代わりに次のようなことができます:(node.js v8.0.0 +)

_const util = require('util');
async function start() {
    let server = Http.createServer(app);
    await util.promisify(server.listen.bind(server))(port);
}
_

util.promisify(some_function)は、通常コールバックを受け入れる関数を取り、代わりにプロミスを返すこの関数の新しいラップされたバージョンを返します。

詳細な手順:

_let server = Http.createServer(app);
// .bind() is needed so that .listen() keeps the correct `this` context when it is called.
// If your function does not require any specific context, leave off .bind()
let listen_promise = util.promisify(server.listen.bind(server));
await listen_promise(port);
_

bluebird を使用すると、より高度な約束をすることができます。

6
JoshWillik

これは、http server listen関数を真に約束しようとしたときにつまずいたものです。最大の問題は、listeningコールバックで解決するのではなく、起動時にエラーを処理することです。

Node.jsサーバー、catch、または派生net/httpは、httpsインスタンスであるため、PromiseでラップしてEventEmitter(他の回答が示唆するように)またはtry-catchブロックを試みても効果はありません。代わりに、errorイベントとして発行されます。

したがって、上記のすべてを考慮すると、約束されたサーバーlisten関数の正しい実装は次のとおりです。

const { createServer } = require('http');

const server = createServer();

const listen = async (port, Host) => {
  return new Promise((resolve, reject) => {
    const listeners = {};

    listeners.onceError = (error) => {
      server.removeListener('listening', listeners.onceListening);
      reject(error);
    };

    listeners.onceListening = () => {
      server.removeListener('error', listeners.onceError);
      resolve();
    };

    server
      .prependOnceListener('error', listeners.onceError)
      .prependOnceListener('listening', listeners.onceListening);

    server.listen(port, Host);
  });
}

ハンドラー内の呼び出しを拒否および解決することは、リスナースタックの先頭に追加され、相互にキャンセルします(最初に起動したユーザー)。

そうすれば、listenメソッドがサーバーを起動するか、キャッチ可能なエラーをスローすることが保証されます。

2
Damaged Organic
const doRequest = () => new Promise((resolve, reject) {
        this.server = Http.createServer(app);
        this.server.listen(port, () => {
            resolve();
        });
    })

async function start() {
 await doRequest()
}

こんな感じ

2
Kejt

私は基本的なユーティリティを作成しました最も適切な方法ではないかもしれませんしかし、方法ですより読みやすい IMO:

// async timout util
const timeout = async ms => new Promise(res => setTimeout(res, ms));

async function start() {
    let output;

    this.server = Http.createServer(app);
    this.server.listen(port, () => {
        output = true; // or can be any data that you want to return
    });
    while (output === undefined) await timeout(10);
    return output;
}

これが基本的な概念です。ただし、carrefulで、promiseが未定義の値を返す可能性がある場合、関数は永久に実行されます(ただし、これはクラッシュしません)。

0
538ROMEO