异步功能阻止执行

时间:2019-06-18 20:25:04

标签: javascript node.js promise

据我了解,异步函数允许异步执行代码,但似乎无法在我的代码中起作用:

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

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

longAsyncWork完成后,在控制台 中写了“ end”,但是我认为异步功能的目标是允许执行代码而不会阻塞主执行。

也许是console.log调用的特殊性使我看不到真相?我可以假设在longAsyncWork函数完成之前执行longAsyncWork调用之后的代码吗?

3 个答案:

答案 0 :(得分:1)

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请求来获取内容,这将需要一些时间。这意味着,cpu现在是空闲的,直到它开始接收结果为止。通过使此功能async,您现在可以在两次之间使用这段时间来执行其他任务。这就是并发在javascript中的工作方式。

第二个函数执行for循环,进行一些计算。这些计算不能是异步的,因为没有办法中断它,没有时间等待,也没有I / O操作。仅仅因为您添加了async关键字,就不能改变只有一个cpu线程正在执行该代码这一事实。

答案 1 :(得分:0)

简单地创建一个函数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)的最终承诺直到主线程完成后才解析,而是在任何await s之前进行同步处理解决后仍会阻止。

如果要确保昂贵的处理不会阻塞主线程,则可以改用网络工作者:

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>

答案 2 :(得分:0)

您的代码的有效版本。

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

按“运行代码段”以查看其正常运行。