是否需要setTimeout?

Pas*_*cal 2 javascript asynchronous settimeout async-await

我有一个async,await和setTimeout()的问题.我想,我使用异步函数来处理慢进程.所以我尝试了一个大循环.在我的计算机上,运行以下代码需要几秒钟:

function slowFunction() {
    return new Promise(resolve => {
        setTimeout(() => {
            for (let i = 0; i < 4000000000; i++) {};
            resolve('Ready at ' + new Date().toLocaleTimeString('de'));
        }, 0);
    });
};


console.log('Start: ' + new Date().toLocaleTimeString('de'));

(async () => {
    console.log('Call slow function.');
    console.log(await slowFunction());
})();

console.log('There is no need to wait for the slow function: ' + new Date().toLocaleTimeString('de'));
Run Code Online (Sandbox Code Playgroud)

输出是:

Start: 16:39:20
Call slow function.
There is no need to wait for the slow function: 16:39:20
Ready at 16:39:23
Run Code Online (Sandbox Code Playgroud)

现在的问题是:下一个代码有什么区别:

function slowFunction() {
    return new Promise(resolve => {
        for (let i = 0; i < 4000000000; i++) {};
        resolve('Ready at ' + new Date().toLocaleTimeString('de'));
    });
};

console.log('Start: ' + new Date().toLocaleTimeString('de'));

(async () => {
    console.log('Call slow function.');
    console.log(await slowFunction());
})();

console.log('There is no need to wait for the slow function: ' + new Date().toLocaleTimeString('de'));
Run Code Online (Sandbox Code Playgroud)

输出是:

Start: 16:39:20
Call slow function.
There is no need to wait for the slow function: 16:39:23
Ready at 16:39:23
Run Code Online (Sandbox Code Playgroud)

从第一个例子看,它看起来像异步.通过第二个例子,函数等待循环结束.

我是否必须使用setTimeout或者代码中是否有错误或者我错了?在这两种情况下,解决方案都是大循环的背后.

async和await的大多数示例都使用了setTimeout,但我认为,它只是模拟一个中断.

感谢您的帮助.

最好迎接Pascal

T.J*_*der 5

TL:DR

Promise和async函数不会将您的代码卸载到另一个线程.如果你想将这个长时间运行的进程从主线程移开,在浏览器上查看web worker,在Node.js上查看子进程.

细节

Promise和async函数(这只是创建和使用promises的语法)不会将您的处理移动到任何其他线程,它仍然发生在您启动该进程的同一个线程上.在只有他们做的事情是确保thencatch回调异步调用.它们不会使您的代码异步(除了那一件事,确保回调异步发生).

所以你的第一个块setTimeout只使用设置超时,返回一个promise,然后当超时到期时,它会在你的慢速运行进程执行时阻塞主线程.这只是在阻塞发生一点点时发生变化,它不会改变阻塞的事实.

您可以在此处查看该效果,注意计数器在长时间运行的进程发生时如何暂停:

function slowFunction() {
  return new Promise(resolve => {
    setTimeout(() => {
      const stop = Date.now() + 2000;
      while (Date.now() < stop) {
        // busy wait (obviously, never really do this)
      }
    }, 1000);
  });
};

console.log("before slowFunction");
slowFunction()
  .then(() => {
    console.log("then handler on slowFunction's promise");
  })
  .catch(console.error);
console.log("after slowFunction");

let counter = 0;
const timer = setInterval(() => {
  console.log(++counter);
}, 100);
setTimeout(() => {
  clearInterval(timer);
  console.log("done");
}, 3000);
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper {
  max-height: 100% !important;
}
Run Code Online (Sandbox Code Playgroud)

你的第二个块不是立即使用setTimeout块,因为promise执行器函数(你传递的函数new Promise)立即和同步运行,而你没有做任何事情来使它异步.

你可以在这里看到; 柜台立即停顿,不迟于:

function slowFunction() {
  return new Promise(resolve => {
    const stop = Date.now() + 2000;
    while (Date.now() < stop) {
      // busy wait (obviously, never really do this)
    }
  });
};

console.log("before slowFunction");
slowFunction()
  .then(() => {
    console.log("then handler on slowFunction's promise");
  })
  .catch(console.error);
console.log("after slowFunction");

let counter = 0;
const timer = setInterval(() => {
  console.log(++counter);
}, 100);
setTimeout(() => {
  clearInterval(timer);
  console.log("done");
}, 3000);
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper {
  max-height: 100% !important;
}
Run Code Online (Sandbox Code Playgroud)

我们甚至没有看到之前的慢功能日志出现,直到长时间运行的代码完成,因为浏览器永远没有机会重新绘制,我们让线程陷入困境.

关于async功能:在该代码async功能开始时同步,并且是同步的,直到第一await(或其他构建体,如setTimeout,即时间表事后执行).只有那之后的代码是异步的(因为它必须等待).

这是一个证明:

async function foo() {
  console.log("before await");
  await Promise.resolve();
  console.log("after await");
}

console.log("before foo");
foo()
  .then(() => {
    console.log("then handler on foo's promise");
  })
  .catch(console.error);
console.log("after foo");
Run Code Online (Sandbox Code Playgroud)

这是输出:

before foo
before await
after foo
after await
then handler on foo's promise

注意在foo 之前await之前是如何发生的; 它与调用同步.但是之后等待直到稍后才会发生(因为必须让它后面的代码异步发生;它的语法糖,即使承诺已经解决,也承诺不会同步调用它的处理程序).fooawait Promise.resolve()then