web-dev-qa-db-ja.com

非同期/待機構文で拒否する方法は?

Async/await関数によって返された約束をどうやって拒否できますか?

例えばもともと

foo(id: string): Promise<A> {
  return new Promise((resolve, reject) => {
    someAsyncPromise().then((value)=>resolve(200)).catch((err)=>reject(400))
  });
}

非同期に変換する/待つ

async foo(id: string): Promise<A> {
  try{
    await someAsyncPromise();
    return 200;
  } catch(error) {//here goes if someAsyncPromise() rejected}
    return 400; //this will result in a resolved promise.
  });
}

それでは、どうすればこのケースでこの約束を正しく拒否できますか?

183
Phoenix

最善の策は、値をラップするthrowErrorを使用することです。その結果、Errorが値をラップするという約束が拒否されます。

} catch (error) {
    throw new Error(400);
}

値をthrowにすることもできますが、スタックトレース情報はありません。

} catch (error) {
    throw 400;
}

あるいは、値を折り返すErrorで、拒否された約束を返します。

} catch (error) {
    return Promise.reject(new Error(400));
}

(あるいはreturn Promise.reject(400);だけですが、コンテキスト情報はありません。)

(あなたの場合、TypeScriptを使用していてfooのretrn値はPromise<A>であるので、return Promise.reject<A>(400 /*or error*/);を使用します)

async/awaitの状況では、それはおそらく少し意味的な不一致ですが、うまくいきます。

あなたがErrorを投げるなら、それはあなたのfooの結果をawaitの構文で消費するものすべてとうまく働きます:

try {
    await foo();
} catch (error) {
    // Here, `error` would be an `Error` (with stack trace, etc.).
    // Whereas if you used `throw 400`, it would just be `400`.
}
215
T.J. Crowder

おそらく、あなたは非同期操作の呼び出しの後にcatch()関数を単に連鎖させることができるということもまた言及されるべきです。

await foo().catch(error => console.log(error));

このようにして、好きでなければtry/catch構文を避けることができます。

109
David

promise を受け取り、エラーがなければデータを、エラーがあればエラーを返す ラッパー関数 を作成できます。

function safePromise(promise) {
  return promise.then(data => [ data ]).catch(error => [ null, error ]);
}

ES7 および async 関数でこのように使用します。

async function checkItem() {
  const [ item, error ] = await safePromise(getItem(id));
  if (error) { return null; } // handle error and return
  return item; // no error so safe to use item
}
9
Andy

Async関数を書くもっと良い方法は、保留中のPromiseを最初から返してから、エラーで拒否されたPromiseを書き出すのではなく、Promiseのコールバック内で拒否と解決の両方を処理することです。例:

async foo(id: string): Promise<A> {
    return new Promise(function(resolve, reject) {
        // execute some code here
        if (success) { // let's say this is a boolean value from line above
            return resolve(success);
        } else {
            return reject(error); // this can be anything, preferably an Error object to catch the stacktrace from this function
        }
    });
}

それから、返されたプロミスでメソッドをチェインするだけです:

async function bar () {
    try {
        var result = await foo("someID")
        // use the result here
    } catch (error) {
        // handle error here
    }
}

bar()

ソース - このチュートリアル:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

4
OzzyTheGiant

これは@ T.Jに対する答えではありません。クラウダーのもの「実際に、例外が却下に変換されるのであれば、それがエラーであるかどうかが実際に気になるかどうかはわかりません。エラーのみをスローする理由はおそらく当てはまりません。 「

あなたのコードがasync/awaitを使用している場合、それでも400の代わりにErrorで拒否するのは良い習慣です:

try {
  await foo('a');
}
catch (e) {
  // you would still want `e` to be an `Error` instead of `400`
}
2
unional

これは昔からの質問ですが、私はスレッドに出会ったばかりで、エラーと却下の間には混乱があるようです。予想される事例に対処する。例えば、非同期メソッドがユーザーを認証しようとして認証が失敗した場合、それは拒否(予想される2つのケースのうちの1つ)であり、エラーではありません(例えば、認証APIが利用できなかった場合)。

私が髪の毛を分割するだけではないことを確認するために、このコードを使用して、それに対する3つの異なるアプローチのパフォーマンステストを実行しました。

const iterations = 100000;

function getSwitch() {
  return Math.round(Math.random()) === 1;
}

function doSomething(value) {
  return 'something done to ' + value.toString();
}

let processWithThrow = function () {
  if (getSwitch()) {
    throw new Error('foo');
  }
};

let processWithReturn = function () {
  if (getSwitch()) {
    return new Error('bar');
  } else {
    return {}
  }
};

let processWithCustomObject = function () {
  if (getSwitch()) {
    return {type: 'rejection', message: 'quux'};
  } else {
    return {type: 'usable response', value: 'fnord'};
  }
};

function testTryCatch(limit) {
  for (let i = 0; i < limit; i++) {
    try {
      processWithThrow();
    } catch (e) {
      const dummyValue = doSomething(e);
    }
  }
}

function testReturnError(limit) {
  for (let i = 0; i < limit; i++) {
    const returnValue = processWithReturn();
    if (returnValue instanceof Error) {
      const dummyValue = doSomething(returnValue);
    }
  }
}

function testCustomObject(limit) {
  for (let i = 0; i < limit; i++) {
    const returnValue = processWithCustomObject();
    if (returnValue.type === 'rejection') {
      const dummyValue = doSomething(returnValue);
    }
  }
}

let start, end;
start = new Date();
testTryCatch(iterations);
end = new Date();
const interval_1 = end - start;
start = new Date();
testReturnError(iterations);
end = new Date();
const interval_2 = end - start;
start = new Date();
testCustomObject(iterations);
end = new Date();
const interval_3 = end - start;

console.log(`with try/catch: ${interval_1}ms; with returned Error: ${interval_2}ms; with custom object: ${interval_3}ms`);

Javascriptインタプリタについての私の不確実性のためにそこにあるもののいくつかが含まれています(私は一度に1つのうさぎの穴を降りるのが好きです)。たとえば、doSomething関数を含め、その戻り値をdummyValueに代入して、条件付きブロックが最適化されないようにします。

私の結果は次のとおりです。

with try/catch: 507ms; with returned Error: 260ms; with custom object: 5ms

小規模な最適化を追求するのは面倒なことではない場合がたくさんあることを私は知っていますが、大規模なシステムではこれらのことが大きな累積的な違いを生む可能性があります。

そう……受け入れられた答えのアプローチは、async関数内で予測不可能なエラーを処理しなければならないと思っている場合には妥当だと思いますが、拒絶が単に「Plan B(またはPlan B(またはC、またはD…) "私の好みはカスタムレスポンスオブジェクトを使って拒否することだと思います。

2
RiqueW

複数のtry-catchブロックを使用せずに、新規のアプローチで 拒否を処理します を適切に提案します。

import to from './to';

async foo(id: string): Promise<A> {
    let err, result;
    [err, result] = await to(someAsyncPromise()); // notice the to() here
    if (err) {
        return 400;
    }
    return 200;
}

to.ts 関数は次の場所からインポートする必要があります。

export default function to(promise: Promise<any>): Promise<any> {
    return promise.then(data => {
        return [null, data];
    }).catch(err => [err]);
}

クレジットは次の link でDima Grossmanに行きます。

0
Pedro Lourenço