为啥等待后的代码没有立即运行?它不应该是非阻塞的吗?

Posted

技术标签:

【中文标题】为啥等待后的代码没有立即运行?它不应该是非阻塞的吗?【英文标题】:Why doesn't the code after await run right away? Isn't it supposed to be non-blocking?为什么等待后的代码没有立即运行?它不应该是非阻塞的吗? 【发布时间】:2017-09-04 06:46:26 【问题描述】:

我很难理解 async 和 await 是如何在幕后工作的。我知道我们有承诺,通过使用“then”函数可以使我们的代码成为非阻塞代码,我们可以在承诺解决后放置我们需要做的所有工作。以及我们想要并行执行的工作以保证我们只是在 then 函数之外编写它。因此代码变得非阻塞。但是我不明白 async await 是如何生成非阻塞代码的。

async function myAsyncFunction() 
  try 
    let data = await myAPICall('https://jsonplaceholder.typicode.com/posts/1');
    // It will not run this line until it resolves await.
    let result = 2 + 2;
    return data;
  catch (ex)
    return ex;
  

参见上面的代码。在解决 API 调用之前,我无法继续前进。如果它使我的代码阻塞代码,那么它怎么比承诺更好?或者我对asyncawait 有什么遗漏吗?我将不依赖于等待调用的代码放在哪里?所以它可以继续工作而无需等待执行?

我正在添加一个我想在异步等待示例中复制的 Promise 代码。

function myPromiseAPI() 
  myAPICall('https://jsonplaceholder.typicode.com/posts/1')
    .then(function (data) 
        // data
    );
   // runs parallel
  let result = 2 + 2;

【问题讨论】:

AFAIK,await 要求它等待的东西是一个承诺。所以这并不是真正的非此即彼。 这有点像“直到它解决”评论之后的行在.then() 中。而且你的整个函数本身就是异步的,所以无论调用什么它都不必等待它。 @Paul 如果不是 Promise,它还会转换 Promise 中的表达式。那是我在博客中读到的 @nnnnnn 是的。就像我把它放在.then()中一样。但是我如何将它放在异步等待场景中的 then() 之外 @RohailNajam 是的,不过,我的意思是关于异步调用。使用期望回调代替该承诺的函数是行不通的。 【参考方案1】:

正如它的名字所暗示的那样,await 关键字将导致函数“等待”,直到它的承诺在执行下一行之前解决。 await 的全部意义在于让代码等到操作完成后再继续。

此代码与阻塞代码的区别在于,在函数等待异步操作完成时,函数外部可以继续执行。

asyncawait 只是 Promise 之上的语法糖。它们允许您编写看起来很像普通同步代码的代码,即使它在幕后使用了 Promise。如果我们将您的示例翻译为明确适用于 Promise 的内容,它将如下所示:

function myAsyncFunction() 
  return myAPICall('https://jsonplaceholder.typicode.com/posts/1')
    .then(function (data) 
       let result = 2 + 2;
       return data;
    )
    .catch(function (ex) 
        return ex;
    );

正如我们在此处看到的,let result = 2 + 2; 行位于.then() 处理程序中,这意味着它在myAPICall() 解决之前不会执行。使用await 时也是如此。 await 只是为你抽象出.then()

要记住的一件事(我认为您正在寻找的重点)是您不必立即使用await。如果你这样写你的函数,那么你可以立即执行你的let result = 2 + 2; 行:

const timeout = 
    seconds => new Promise(res => setTimeout(res, seconds * 1000));

function myAPICall() 
  // simulate 1 second wait time
  return timeout(1).then(() => 'success');


async function myAsyncFunction() 
  try 
    console.log('starting');

    // just starting the API call and storing the promise for now. not waiting yet
    let dataP = myAPICall('https://jsonplaceholder.typicode.com/posts/1');

    let result = 2 + 2;

    // Executes right away
    console.log('result', result);

    // wait now
    let data = await dataP;

    // Executes after one second
    console.log('data', data);

    return data;
   catch (ex) 
    return ex;
  


myAsyncFunction();

经过一些澄清后,我可以看到您真正想知道的是如何避免必须一个一个地等待两个异步操作,而是让它们并行执行。事实上,如果你一个接一个地使用await,第二个在第一个完成之前不会开始执行:

const timeout = 
    seconds => new Promise(res => setTimeout(res, seconds * 1000));

function myAPICall() 
  // simulate 1 second wait time
  return timeout(1).then(() => 'success');


async function myAsyncFunction() 
  try 
    console.log('starting');

    let data1 = await myAPICall('https://jsonplaceholder.typicode.com/posts/1');
    // logs after one second
    console.log('data1', data1);

    let data2 = await myAPICall('https://jsonplaceholder.typicode.com/posts/2');
    // logs after one more second
    console.log('data2', data2);
   catch (ex) 
    return ex;
  


myAsyncFunction();

为了避免这种情况,你可以做的是开始两个异步操作,通过执行它们而不等待它们,将它们的承诺分配给一些变量。然后你可以等待两个承诺:

const timeout = 
    seconds => new Promise(res => setTimeout(res, seconds * 1000));

function myAPICall() 
  // simulate 1 second wait time
  return timeout(1).then(() => 'success');


async function myAsyncFunction() 
  try 
    console.log('starting');
    // both lines execute right away
    let dataP1 = myAPICall('https://jsonplaceholder.typicode.com/posts/1');
    let dataP2 = myAPICall('https://jsonplaceholder.typicode.com/posts/2');

    let data1 = await dataP1;
    let data2 = await dataP2;

    // logs after one second
    console.log('data1', data1);
    console.log('data2', data2);
   catch (ex) 
    return ex;
  


myAsyncFunction();

另一种方法是使用 Promise.all() 和一些数组分解:

const timeout = 
    seconds => new Promise(res => setTimeout(res, seconds * 1000));

function myAPICall() 
  // simulate 1 second wait time
  return timeout(1).then(() => 'success');


async function myAsyncFunction() 
  try 
    console.log('starting');

    // both myAPICall invocations execute right away
    const [data1, data2] = await Promise.all([
        myAPICall('https://jsonplaceholder.typicode.com/posts/1'), 
        myAPICall('https://jsonplaceholder.typicode.com/posts/2'),
    ]);

    // logs after one second
    console.log('data1', data1);
    console.log('data2', data2);
   catch (ex) 
    return ex;
  


myAsyncFunction();

【讨论】:

我明白这一点。我的意思是,如果我们想与这个 api 调用并行运行任何东西,我们只需将它放在 then 函数旁边,就像我在编辑的问题中所做的那样。我将如何在异步等待中实现这种“并行性” @RohailNajam 请查看我的更新。您可以根据需要并行“启动”任意数量的 Promise。一旦你使用await,函数就会开始等待他们的结果。 @RohailNajam 是的,没错。我已经修改了我的答案,以展示如何使用数组分解将其拆分为各自的值。 “函数外的世界将继续执行,而这个函数正在做它的事情” “外面的世界”将在函数运行时继续执行什么都不做 @zeroflagL 确实是个挑剔的人,但也足够公平。我已经修正了措辞。

以上是关于为啥等待后的代码没有立即运行?它不应该是非阻塞的吗?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 JTable CellRenderer 一直在运行?

知乎为啥使用Tornado?使用Python中的多线程特性了吗

此异步等待不应该工作。为啥它会起作用?

同步一步阻塞非阻塞

求解为啥qt5.7.0缺少对应的编译器

fcntl函数