<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise关键问题 - Promise 改变状态与指定回调的顺序问题</title>
</head>
<body>
<script>
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
setTimeout(() => {
console.log('23')
}, 1000);
});
p.then(value => {
console.log(value);
},reason=>{
})
</script>
</body>
</html>
在您提供的HTML和JavaScript示例中,存在两个setTimeout
调用,它们都被设置在Promise
的构造函数执行器内部,并且都设置了1000毫秒的延迟。这里的关键是理解JavaScript的事件循环和异步执行机制如何影响这些操作的执行顺序。
- Promise的构造函数执行:
- 当
Promise
的构造函数被调用时,它的执行器函数会立即执行。 - 在这个执行器函数内部,您定义了两个
setTimeout
调用,它们都将在1秒后执行。
- 当
- setTimeout的排队:
setTimeout
会将其回调函数放入JavaScript的异步任务队列(也称为宏任务队列)中,等待指定的时间后执行。- 在这个例子中,两个
setTimeout
的回调函数都被安排在大约1秒后执行(实际上,它们会尽可能接近地同时到达执行点,但JavaScript引擎会确保它们按照被添加到任务队列的顺序执行)。
- Promise的状态变化:
- 第一个
setTimeout
的回调函数会调用resolve('OK')
,这会导致Promise
对象p
从pending状态变为fulfilled状态。 - 这个状态变化会触发
.then()
方法中注册的回调函数(处理fulfilled状态的)的执行。但是,这个回调函数不会立即执行;它会被放入一个微任务队列中,等待当前宏任务完成后执行。
- 第一个
- 执行顺序:
- 当JavaScript引擎完成当前执行栈中的所有同步代码后(在这个例子中,同步代码就是
Promise
的构造函数和.then()
方法的调用),它会查看宏任务队列。 - 由于两个
setTimeout
的回调函数都已经被添加到宏任务队列中,并且它们的时间都已到,JavaScript引擎会按照它们被添加到队列的顺序来执行它们。 - 第一个
setTimeout
的回调函数会先执行,调用resolve('OK')
,改变Promise
的状态,并触发微任务队列中的.then()
回调函数。但是,由于当前正在执行宏任务(即setTimeout
的回调),微任务队列中的回调函数会等待直到当前宏任务完成。 - 一旦当前宏任务(即第一个
setTimeout
的回调)完成,JavaScript引擎会查看并处理微任务队列中的所有任务。这时,.then()
的回调函数会被执行,并输出'OK'
。 - 随后,第二个
setTimeout
的回调函数执行,打印23
到控制台。
- 当JavaScript引擎完成当前执行栈中的所有同步代码后(在这个例子中,同步代码就是
因此,尽管两个setTimeout
的回调函数都被安排在1秒后执行,但您会看到'OK'
首先被打印到控制台,然后是23
。这是因为.then()
的回调函数是在微任务队列中执行的,它会在当前宏任务(即第一个setTimeout
的回调)完成后立即执行,而第二个setTimeout
的回调则是作为下一个宏任务来执行的。