спробуйте / ловити блоки з асинхронізуванням / очікувати


116

Я копаюсь у функцію асинхрон / очікування вузла 7 і продовжую натикатися на такий код

function getQuote() {
  let quote = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
  return quote;
}

async function main() {
  try {
    var quote = await getQuote();
    console.log(quote);
  } catch (error) {
    console.error(error);
  }
}

main();

Це здається єдиною можливістю вирішити / відхилити або повернути / кинути за допомогою async / wait, проте v8 не оптимізує код у блоках try / catch ?!

Чи є альтернативи?


Що означає "кидок після очікування не є успішним"? Якщо це помилки? Якщо це не поверне очікуваного результату? Ви могли б повторно скинутись у блок вилову.
DevDig

afaik v8 do оптимізувати спробу / ловити, заява на кидок - повільна
Тамас Хегедус

1
Я досі не розумію питання. Ви використовуєте старий ланцюжок обіцянок, але я не думаю, що це буде швидше. Тож ви стурбовані ефективністю пробного лову? Тоді, що це стосується асинхронізації?
Тамас Гегед

Перевірте свою відповідь. Я намагався отримати більш чистий підхід
zardilior

Тут ви можете зробити це stackoverflow.com/a/61833084/6482248 Це виглядає чистіше
Пратамеш Більше

Відповіді:


133

Альтернативи

Альтернатива цьому:

async function main() {
  try {
    var quote = await getQuote();
    console.log(quote);
  } catch (error) {
    console.error(error);
  }
}

було б щось подібне, використовуючи обіцянки прямо:

function main() {
  getQuote().then((quote) => {
    console.log(quote);
  }).catch((error) => {
    console.error(error);
  });
}

або щось подібне, використовуючи стиль продовження проходження:

function main() {
  getQuote((error, quote) => {
    if (error) {
      console.error(error);
    } else {
      console.log(quote);
    }
  });
}

Оригінальний приклад

Ваш оригінальний код - це призупинити виконання та чекати, коли обіцянка повернеться getQuote(). Потім він продовжує виконання і записує повернене значення до, var quoteа потім роздруковує його, якщо обіцянка була вирішена, або викидає виняток і запускає блок лову, який друкує помилку, якщо обіцянку було відхилено.

Можна зробити те ж саме, використовуючи Promise API безпосередньо, як у другому прикладі.

Продуктивність

Тепер про виставу. Давайте перевіримо!

Я щойно написав цей код - f1()дає 1як зворотне значення, f2()кидає 1як виняток:

function f1() {
  return 1;
}

function f2() {
  throw 1;
}

Тепер назвемо той же код мільйон разів, спочатку з f1():

var sum = 0;
for (var i = 0; i < 1e6; i++) {
  try {
    sum += f1();
  } catch (e) {
    sum += e;
  }
}
console.log(sum);

А потім перейдемо f1()до f2():

var sum = 0;
for (var i = 0; i < 1e6; i++) {
  try {
    sum += f2();
  } catch (e) {
    sum += e;
  }
}
console.log(sum);

Це результат, який я отримав для f1:

$ time node throw-test.js 
1000000

real    0m0.073s
user    0m0.070s
sys     0m0.004s

Це те, що я отримав f2:

$ time node throw-test.js 
1000000

real    0m0.632s
user    0m0.629s
sys     0m0.004s

Здається, що ви можете зробити щось на кшталт 2 мільйонів кидків секунди за один однонитковий процес. Якщо ви робите більше цього, то, можливо, вам доведеться турбуватися про це.

Підсумок

Я б не турбувався про подібні речі в Node. Якщо подібні речі звикають багато, то з часом V8 або SpiderMonkey або Chakra будуть оптимізовані, і всі будуть слідувати - це не так, як це не оптимізовано як принцип, це просто не проблема.

Навіть якщо це не оптимізовано, я все-таки стверджую, що якщо ви максимізуєте свій процесор у Node, то, ймовірно, ви повинні записати свій номер, який розсипається в C - ось для чого, серед іншого, і є рідні аддони. А може, такі речі, як node.native , краще підійдуть для роботи, ніж Node.js.

Мені цікаво, який би був корисний випадок, коли потрібно кинути стільки винятків. Зазвичай викидання виключення замість повернення значення - це, ну, виняток.


Я знаю, що код легко можна записати за допомогою Promises, як уже згадувалося, я бачив його навколо на різних прикладах, тому я прошу. Виконання однієї операції в режимі try / catch може не бути проблемою, але може бути кілька функцій асинхронізації / очікування з подальшою логікою програми.
Патрік

4
@Patrick "може бути" і "буде" - це різниця між спекуляцією і власне тестуванням. Я перевірив це на одне твердження, тому що це було у вашому запитанні, але ви можете легко перетворити мої приклади, щоб перевірити наявність кількох тверджень. Я також надав кілька інших варіантів написання асинхронного коду, про який ви також запитували. Якщо він відповідає на ваше запитання, ви можете розглянути можливість прийняття відповіді . Підсумовуючи це: звичайно, винятки є повільнішими, ніж віддача, але їх використання повинно бути винятком.
rsp

1
Кидання винятку насправді повинно бути винятком. При цьому, код неоптимізовано, кидали ви виняток чи ні. Хіт продуктивності походить від використання try catch, а не від викидів винятків. Хоча цифри невеликі, за вашими тестами це майже в 10 разів повільніше, що не є незначним.
Nepoxx

22

Альтернатива, аналогічна поводженню з помилками в Golang

Оскільки async / await використовує обіцянки під кришкою, ви можете написати невелику функцію утиліти, як це:

export function catchEm(promise) {
  return promise.then(data => [null, data])
    .catch(err => [err]);
}

Потім імпортуйте його, коли вам потрібно ввести деякі помилки, і оберніть свою функцію асинхронізації, яка повертає обіцянку.

import catchEm from 'utility';

async performAsyncWork() {
  const [err, data] = await catchEm(asyncFunction(arg1, arg2));
  if (err) {
    // handle errors
  } else {
    // use data
  }
}

Я створив пакет NPM, який робить саме зазначене вище - npmjs.com/package/@simmo/task
Майк

2
@Mike Ви можете переробити колесо - вже є популярний пакет, який робить саме це: npmjs.com/package/await-to-js
Якуб Кукул

21

Альтернативою блоку спробу ловлі є lib очікування до js . Я часто його використовую. Наприклад:

import to from 'await-to-js';

async function main(callback) {
    const [err,quote] = await to(getQuote());
    
    if(err || !quote) return callback(new Error('No Quote found'));

    callback(null,quote);

}

Цей синтаксис набагато чіткіше порівняно з пробним уловом.


Спробував це і полюбив. Очистити та читати код за рахунок встановлення нового модуля. Але якщо ви плануєте написати багато асинхронних функцій, я повинен сказати, що це чудове доповнення! Спасибі
filipbarak

15
async function main() {
  var getQuoteError
  var quote = await getQuote().catch(err => { getQuoteError = err }

  if (getQuoteError) return console.error(err)

  console.log(quote)
}

Крім того, замість того, щоб оголосити можливий var, щоб помилка була у верхній частині, яку ви можете зробити

if (quote instanceof Error) {
  // ...
}

Хоча це не спрацює, якщо щось на зразок помилки TypeError або Reference викинуто. Ви можете переконатися, що це звичайна помилка, хоча з

async function main() {
  var quote = await getQuote().catch(err => {
    console.error(err)      

    return new Error('Error getting quote')
  })

  if (quote instanceOf Error) return quote // get out of here or do whatever

  console.log(quote)
}

Моя перевага для цього полягає в тому, щоб загортати все у великий блок-пробник, де створюється багато обіцянок, це може зробити громіздкою обробку помилки спеціально до обіцянки, яка її створила. З альтернативою є декілька блоків, які намагаються наздогнати, які я вважаю однаково громіздкими


8

Більш чистою альтернативою буде наступна:

Через те, що кожна асинхронна функція технічно є перспективною

Ви можете додавати улов у функції, коли дзвоните їм у режимі очікування

async function a(){
    let error;

    // log the error on the parent
    await b().catch((err)=>console.log('b.failed'))

    // change an error variable
    await c().catch((err)=>{error=true; console.log(err)})

    // return whatever you want
    return error ? d() : null;
}
a().catch(()=>console.log('main program failed'))

Не потрібно намагатися ловити, оскільки всі обіцянки помилок обробляються, і у вас немає помилок коду, ви можете опустити це у батьківській програмі !!

Скажімо, ви працюєте з mongodb, якщо виникла помилка, ви можете скористатися функцією, яка викликає її, ніж виготовлення обгортки або використання пробного лову.


У вас є 3 функції. Один отримує значення та фіксує помилку, інший ви повертаєте, якщо немає помилки, і, нарешті, дзвінок до першої функції із зворотним дзвоном, щоб перевірити, чи повернув цей помилку. Все це вирішується одним "обіцянкою" .then (cb) .catch (cb) або блоком trycatch.
Головний коші

@Chajakoshi Як ви бачите, жоден улов не зробить, оскільки помилка обробляється по-різному у всіх трьох випадках. Якщо перший не вдається, він повертає d (), якщо другий не дає, він повертає нульовим, якщо останній не дає іншого повідомлення про помилку. Питання запитує про помилки обробки під час використання системи wait. Так що і відповідь. Усі повинні виконуватись, якщо якісь не вдаються. Спробуйте блоки лову потребують трьох з них у цьому конкретному прикладі, який не є чистішим
zardilior

1
Питання не вимагає виконання після невиконаних обіцянок. Тут ви чекаєте B, потім запустіть C і поверніть D, якщо вони помилилися. Як це очищувач? С доводиться чекати B, але вони не залежать один від одного. Я не бачу причини, чому вони будуть в А разом, якщо вони незалежні. Якщо вони залежать один від одного, ви хочете зупинити виконання C, якщо B виходить з ладу, завданням .then.catch або try-catch. Я припускаю, що вони нічого не повертають і виконують деякі асинхронні дії, абсолютно не пов'язані з А. Чому їх викликають за допомогою асинхронізації?
Головний коші

Питання стосується альтернатив, щоб спробувати блоки лову для обробки помилок при використанні async / wait. Приклад тут повинен бути описовим і є не що інше, як приклад. Він показує індивідуальне керування незалежними операціями послідовно, як правило, як використовується асинхронізація / очікування. Чому їх називають за допомогою асинхронізації в очікуванні, це просто показати, як з цим можна було впоратися. Його описовий характер більш ніж виправданий.
zardilior

2

Я хотів би зробити так :)

const sthError = () => Promise.reject('sth error');

const test = opts => {
  return (async () => {

    // do sth
    await sthError();
    return 'ok';

  })().catch(err => {
    console.error(err); // error will be catched there 
  });
};

test().then(ret => {
  console.log(ret);
});

Це схоже на помилку обробки co

const test = opts => {
  return co(function*() {

    // do sth
    yield sthError();
    return 'ok';

  }).catch(err => {
    console.error(err);
  });
};

Код не дуже зрозумілий чоловік, хоч виглядає цікаво, ви могли б редагувати?
zardilior

Прикро, що в цій відповіді немає ніяких пояснень, оскільки це насправді демонструє чудовий спосіб уникнути пробного лову кожного конкурсу, який ви призначите await!
Джим

0

catchІнфікування таким чином, на мій досвід, небезпечно. Будь-яка помилка, викинута на весь стек, буде зафіксована, а не лише помилка з цієї обіцянки (що, мабуть, не те, що ви хочете).

Другий аргумент обіцянки - це вже відкликання / відмова відмови. Краще і безпечніше використовувати це замість цього.

Ось такий шрифт typesafe, який я написав для цього:

function wait<R, E>(promise: Promise<R>): [R | null, E | null] {
  return (promise.then((data: R) => [data, null], (err: E) => [null, err]) as any) as [R, E];
}

// Usage
const [currUser, currUserError] = await wait<GetCurrentUser_user, GetCurrentUser_errors>(
  apiClient.getCurrentUser()
);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.