Jak odrzucić składnię async/wait?

Jak mogę odrzucić obietnicę, która zwrócona przez async/await funkcja?

Np. oryginalnie:

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

Przetłumaczyć na async/await:

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.
  });
}
Jak więc właściwie mogę odrzucić tę obietnicę w tym przypadku?
Author: Mike B., 2017-02-25

7 answers

Najlepszym rozwiązaniem jest throw AN Error owijanie wartości, co skutkuje odrzuceniem obietnicy z Error owijanie wartości:

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

Możesz również po prostu throw wartość, ale wtedy nie ma informacji o śledzeniu stosu:

} catch (error) {
    throw 400;
}

Na przemian, zwraca odrzuconą obietnicę z Error owijającą wartość, ale nie jest to idiomatyczne:

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

(lub po prostu return Promise.reject(400);, ale znowu, wtedy nie ma informacji kontekstowej.)

W Twoim przypadku, ponieważ używasz TypeScript i foo's return wartość to Promise<A>, możesz użyć tego:

return Promise.reject<A>(400 /*or Error*/ );

W async/await sytuacja, że ostatni jest prawdopodobnie trochę semantyczny błąd dopasowania, ale to działa.

Jeśli rzucisz Error, to dobrze gra z wszystkim, co pochłania Twój wynik foo z await składnia:

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`.
}
 388
Author: T.J. Crowder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-11-21 13:12:28

Należy również wspomnieć, że można po prostu połączyć funkcję catch() po wywołaniu operacji asynchronicznej, ponieważ pod maską nadal zwracana jest obietnica.

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

W ten sposób możesz uniknąć składni try/catch, jeśli ci się nie podoba.

 162
Author: David,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-12 09:48:02

Możesz utworzyć funkcję wrappera , która przyjmuje obietnicę i zwraca tablicę z danymi, jeśli nie ma błędu i błąd, jeśli wystąpił błąd.

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

Użyj go tak w ES7 i w async funkcji:

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
}
 12
Author: Andrew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-17 01:47:46

Lepszym sposobem napisania funkcji asynchronicznej byłoby zwrócenie oczekującej obietnicy od początku, a następnie obsługa zarówno odrzuceń, jak i rezolucji w wywołaniu zwrotnym obietnicy, a nie tylko wypluwanie odrzuconej obietnicy na błąd. Przykład:

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
        }
    });
}

Następnie po prostu łącz metody na zwróconej obietnicy:

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

bar()

Źródło-ten tutorial:

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

 9
Author: OzzyTheGiant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-12 14:25:02

To nie jest odpowiedź na @T. J. Crowder. Po prostu komentarz odpowiadający komentarzowi " a właściwie, jeśli wyjątek ma zostać zamieniony na odrzucenie, nie jestem pewien, czy faktycznie przeszkadza mi, czy to błąd. Moje powody rzucania tylko błąd prawdopodobnie nie mają zastosowania."

Jeśli Twój kod używa async/await, następnie nadal dobrą praktyką jest odrzucenie za pomocą Error zamiast 400:

try {
  await foo('a');
}
catch (e) {
  // you would still want `e` to be an `Error` instead of `400`
}
 6
Author: unional,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-25 08:42:44

Mam sugestię, aby prawidłowo radzić sobie z odrzuceniami w nowatorskim podejściu, bez konieczności wielokrotnego blokowania 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;
}

Gdzie do.funkcja ts powinna być importowana z:

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

Napisy trafiają do Dima Grossmana w poniższym linku .

 5
Author: Pedro Lourenço,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-09 06:14:22

Wiem, że to stare pytanie, ale po prostu natknąłem się na wątek i wydaje się, że istnieje tutaj konflikt między błędami a odrzuceniem, który działa (przynajmniej w wielu przypadkach) często powtarzanych rad, aby nie używać obsługi wyjątków do czynienia z przewidywanymi przypadkami. Aby zilustrować: jeśli metoda asynchroniczna próbuje uwierzytelnić użytkownika, a uwierzytelnianie nie powiedzie się, jest to odrzucenie (jeden z dwóch przewidywanych przypadków), a nie błąd (np. niedostępny.)

Aby upewnić się, że nie tylko dzielę włosy, przeprowadziłem test wydajności trzech różnych podejść do tego, używając tego kodu:

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`);

Niektóre rzeczy, które tam są, są zawarte ze względu na moją niepewność co do interpretera Javascript (Lubię tylko schodzić do jednej króliczej nory na raz); na przykład, włączyłem funkcję doSomething i przypisałem jej powrót do dummyValue, aby upewnić się, że bloki warunkowe nie zostaną zoptymalizowane.

Moje wyniki były:

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

Wiem, że jest wiele przypadków, w których nie warto szukać małych optymalizacji, ale w systemach na większą skalę te rzeczy mogą mieć dużą kumulacyjną różnicę, a to dość surowe porównanie.

Więc ... chociaż myślę, że podejście zaakceptowanej odpowiedzi jest dobre w przypadkach, w których spodziewasz się, że będziesz musiał poradzić sobie z nieprzewidywalnymi błędami w funkcji asynchronicznej, w przypadkach, w których odrzucenie oznacza po prostu " będziesz musiał iść z planem B (lub C, lub D...) " myślę, że moją preferencją byłoby odrzucenie za pomocą niestandardowego obiektu odpowiedzi.

 3
Author: RiqueW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-31 06:32:08