Асинхронная функция блокирует выполнение - PullRequest
0 голосов
/ 18 июня 2019

Насколько я понимаю, асинхронная функция позволяет выполнять код асинхронно, но в моем коде она не работает:

async function longAsyncWork() {
    await foo(); // takes about 10 seconds to complete
}

console.log('start');
longAsyncWork();
console.log('end');

'end' записывается в консоли после , когда longAsyncWork завершил, но я думал, что целью асинхронных функций было позволить выполнить код, не блокируя основное выполнение.

Может быть, это особенность вызова console.log, который мешает мне увидеть правду? Можно ли предположить, что код после вызова longAsyncWork будет выполнен до завершения функции longAsyncWork?

Ответы [ 3 ]

1 голос
/ 18 июня 2019

Функции Asnyc работают асинхронно, но javascript является однопоточным, что означает, что браузер может выполнять только одну задачу за раз.То, что вы делаете внутри функции, должно быть асинхронным по своей природе, чтобы фактически заставить ее работать, однако вы, вероятно, выполнили некоторую блокирующую операцию, которая является синхронной по своей природе.Позвольте мне объяснить более подробно, приведя пример:

async function fooThatCanBeAsync(){
    var result = await fetch("https://stackoverflow.com");
      console.log("after async task 1");
      return result;
    }
    async function fooThatCannotBeAsync(){
        var x = 0;
        for(i=0; i<10000000; i++){
            x += i;
        }
      console.log("after async task 2");
   }
   fooThatCanBeAsync();
   fooThatCannotBeAsync();
   console.log("end...");

Приведенный выше глупый пример имеет две асинхронные функции.Однако только один из них может работать асинхронно!Первая функция пытается что-то получить, отправив http-запрос, и это займет некоторое время.Это означает, что процессор теперь свободен, пока не начнет получать результат.Сделав эту функцию async, вы теперь можете использовать это промежуточное время для выполнения других задач.Вот как работает параллелизм в javascript.

Вторая функция выполняет цикл for, выполняя некоторые вычисления.Эти вычисления не могут быть асинхронными, потому что нет способа прервать их, нет времени ожидания между ними и нет операций ввода-вывода.Просто добавив ключевое слово async, вы не сможете изменить тот факт, что только один поток ЦП выполняет этот код.

0 голосов
/ 18 июня 2019

Рабочая версия вашего кода.

const sleep = ms => new Promise(r => setTimeout(r, ms))

async function longAsyncWork() {
  // await resolves the sleep promise, so it lock longAsyncWork.
  await sleep(1000)
  console.log('async')
}

console.log('start');

// async longAsyncWork is a promise, so if you doesn't lock it with await, it will run async.
longAsyncWork();
console.log('end');	

нажмите Запустить фрагмент кода, чтобы увидеть, как он работает.

0 голосов
/ 18 июня 2019

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

const foo = () => new Promise((resolve) => {
  for (let i = 0; i < 1e9; i++) {
  }
  console.log('foo expensive sync work done');
  resolve();
});
async function longAsyncWork() {
    await foo(); // takes about 10 seconds to complete
}

console.log('start');
longAsyncWork();
console.log('end, still on main thread');

Окончательное Обещание, что вызов longAsyncWork разрешается (или любые await s внутри), не будет разрешаться до тех пор, пока не завершится основной поток, но синхронная обработка до разрешения любых await все равно будет блокироваться.

Если вы хотите убедиться, что дорогостоящая обработка не блокирует основной поток, вы можете вместо этого использовать веб-работника:

const workerFn = () => {
  self.onmessage = () => {
    for (let i = 0; i < 1e9; i++) {
    }
    self.postMessage('done');
  }
};
const workerFnStr = `(${workerFn})();`;
const blob = new Blob([workerFnStr], { type: 'text/javascript' });
const worker = new Worker(window.URL.createObjectURL(blob));
worker.onmessage = (result) => {
  console.log('result:', result.data);
};

console.log('about to post message');
worker.postMessage('arg');
console.log('main thread continuing here');

Вы также можете вызвать Promise.resolve() перед запуском longAsyncWork, но в отличие от рабочей версии, он все равно заблокирует поток родительского окна (хотя это будет после того, как основной поток завершится ). Это часто нежелательно, потому что это может помешать пользователю взаимодействовать со страницей - веб-работник часто является лучшим выбором.

const foo = () => new Promise((resolve) => {
  for (let i = 0; i < 1e10; i++) {
  }
  console.log('foo expensive sync work done');
  resolve();
});
async function longAsyncWork() {
    await foo(); // takes about 10 seconds to complete
}

console.log('start');
Promise.resolve()
  .then(longAsyncWork);
console.log('end, still on main thread');
<p>Page won't display for a while, or if page is already displayed, scrolling won't work, because main thread is blocked</p>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...