Синхронные функции выполняются быстрее, чем их асинхронные аналоги?

#javascript #asynchronous

#javascript #асинхронный

Вопрос:

Рассмотрим два приведенных ниже фрагмента:

 const loop1NoPromise = () => {
    let i = 0
    for (i; i < 500000; i  ) {}
}

const loop2NoPromise = () => {
    let i = 0
    for (i; i < 500000; i  ) {}
}

const startNoPromise = () => {
    console.time('No promise')
  
    loop1NoPromise()
    loop2NoPromise()
  
    console.timeEnd('No promise')
}

startNoPromise() 
 const loop1Promise = async () => {
    let i = 0
    for (i; i < 500000; i  ) {}

    return new Promise((resolve) => resolve());
}

const loop2Promise = async () => {
    let i = 0
    for (i; i < 500000; i  ) {}
  
    return new Promise((resolve) => resolve());
}

const startPromise = async () => {
    console.time('With promise')

    await Promise.all([loop1Promise(), loop2Promise()])

    console.timeEnd('With promise')
}

startPromise() 

Мне было интересно, может ли делегирование нескольких функций, которые не предназначались Promise для использования, веб-API-интерфейсам браузера, а затем await их редактирование каким-либо образом повысить производительность. Приступая к этому эксперименту, я наполовину ожидал startPromise , что они будут работать немного быстрее, startNoPromise а другая половина меня ожидала, что они будут выполняться / — одинаково.

Однако выполнение приведенных ниже фрагментов по отдельности показывает, что startNoPromise это значительно быстрее, чем startPromise . Что для меня странно, так это то, что если я объединю эти два фрагмента в один фрагмент, а затем выполню startNoPromise и startPromise , тогда они будут выполняться более или менее одинаково быстро… но при их индивидуальном запуске разница во времени составляет ~ 1 мс, при startNoPromise этом тактовая частота постоянно составляет около 2,245 мс.

Мой вопрос в том, почему моя первоначальная логика была ошибочной, что превращение функций, не связанных с обещаниями, в promises и передача их на аутсорсинг веб-API заставили бы их работать быстрее (потому что поэтому они будут выполняться асинхронно)? Кроме того, почему Promise версия этих двух функций выполняется с меньшей скоростью, чем их синхронные аналоги?

Комментарии:

1. Асинхронные функции используют совместную многозадачность . Они не выполняются параллельно . Они не могут. Одновременно может выполняться только одна вещь. Ваши два цикла не уступают друг другу; первый, который запускается, выполняется до завершения, а затем запускается другой. Что ничем не отличается от кода синхронизации, за исключением того, что у него есть некоторые дополнительные накладные расходы.

2. я бы скорее сказал, что ваш код выполняется «с задержкой», а не «асинхронно».

3. исследуйте веб-работников

Ответ №1:

«Проблема» с JavaScript заключается в том, что все выполняется в одном потоке, т. Е. Одновременно можно выполнить только одну вещь. Использование promises для проблем, связанных с процессором (код, ограниченный мощностью процессора), на самом деле замедлит его. Причина замедления заключается в том, что вызовы API для обещаний и управление обещаниями также требуют затрат процессора.

Еще одна вещь, которую следует отметить, это то, что с функциями, связанными с процессором, без каких-либо ожиданий, эти функции будут выполняться последовательно (одна за другой) в любом случае. С другой стороны, добавление await сделает его еще медленнее, поскольку будет больше накладных расходов на управление.

Обещания отлично подходят для кода, где вам приходится ждать завершения чего-либо вне основного процесса. Чаще всего это будет ввод-вывод.

Ответ №2:

Оба фрагмента являются синхронными вычислениями.

Возврат обещания не делает предыдущие вычисления «асинхронными»: он просто возвращает объект, представляющий асинхронный результат. Это (в данном случае уже решенное) обещание.

В вашем втором сокращенном коде добавляемый вами код только обертывает результат в обещание, разрешение которого не может быть обработано до следующего цикла цикла событий.

Другими словами: когда вы это делаете await Promise.all(...) , вы сообщаете движку JS, чтобы он посещал все ожидающие события и в следующем цикле проверял, разрешены ли все обещания, и получал их значение или продолжал ждать в противном случае и так далее.

Если вы удалите ключевое слово await, вы увидите почти идентичные результаты.

Вы также можете обернуть Promise.all(...) выражение в a console.log(...) , и вы увидите это значение (ожидающее обещание).