promise 跟 async/await 在 eventloop 中的调用机制

Promise 和 async/await 是 JavaScript 中进行异步编程的两种重要方式。要理解它们在 event loop 中的调用机制,需要深入了解 JavaScript 的执行模型,包括 call stack(调用栈)、event loop(事件循环)、microtask queue(微任务队列)和 macrotask queue(宏任务队列)。

JavaScript 执行模型

JavaScript 是单线程的,这意味着它一次只能执行一个任务。为了实现异步操作,JavaScript 使用了一个基于事件驱动的模型,event loop。这个模型的主要组成部分包括:

  1. Call Stack:调用栈,用于跟踪正在执行的函数。
  2. Web APIs:浏览器提供的 API,例如 setTimeout、DOM 事件等。
  3. Task Queue:任务队列,用于存储异步任务的回调。
  4. Event Loop:事件循环,负责协调调用栈和任务队列的执行。

Promise 和 Event Loop

Promise 是一个表示异步操作最终完成或失败的对象。它有三种状态:pending(待定)、fulfilled(已完成)和 rejected(已拒绝)。Promise 的主要特点是它能够链接多个异步操作,使得代码更具可读性。

当 Promise 产生时,它会立即执行内部代码,但 then 和 catch 中的回调会被放入 microtask queue。

示例:
console.log('script start');

const promise1 = new Promise((resolve, reject) => {
  console.log('promise1 start');
  resolve('promise1 result');
}).then(result => {
  console.log(result);
});

console.log('script end');

在这个例子中,执行顺序如下:

  1. console.log('script start') 被放入调用栈并执行。
  2. new Promise 被放入调用栈,console.log('promise1 start') 被执行。
  3. resolve('promise1 result') 被调用,但 then 回调被放入 microtask queue。
  4. console.log('script end') 被放入调用栈并执行。
  5. 调用栈清空后,event loop 检查 microtask queue 并执行 then 回调。

最终输出顺序是:

script start
promise1 start
script end
promise1 result

async/await 和 Event Loop

async/await 是 ES2017 引入的一种更简洁的异步编程方式。async 函数返回一个 Promise,await 可以暂停 async 函数的执行,等待 Promise 被解决。

示例:
console.log('script start');

async function asyncFunction() {
  console.log('asyncFunction start');
  const result = await new Promise((resolve, reject) => {
    console.log('promise start');
    resolve('promise result');
  });
  console.log(result);
}

asyncFunction();

console.log('script end');

在这个例子中,执行顺序如下:

  1. console.log('script start') 被放入调用栈并执行。
  2. asyncFunction 被放入调用栈并执行,console.log('asyncFunction start') 被执行。
  3. new Promise 被放入调用栈,console.log('promise start') 被执行。
  4. resolve('promise result') 被调用,但 await 暂停了 asyncFunction,回调被放入 microtask queue。
  5. console.log('script end') 被放入调用栈并执行。
  6. 调用栈清空后,event loop 检查 microtask queue 并执行 await 之后的代码。

最终输出顺序是:

script start
asyncFunction start
promise start
script end
promise result

更复杂的示例

为了更深入地理解 Promise 和 async/await 在 event loop 中的调用机制,可以看一个更复杂的示例。

示例:
console.log('script start');

setTimeout(() => {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(() => {
  console.log('promise1');
}).then(() => {
  console.log('promise2');
});

async function asyncFunction() {
  console.log('asyncFunction start');
  await Promise.resolve();
  console.log('asyncFunction end');
}

asyncFunction();

console.log('script end');

执行顺序如下:

  1. console.log('script start') 被放入调用栈并执行。
  2. setTimeout 回调被放入 macrotask queue。
  3. Promise.resolve().then 被放入 microtask queue。
  4. asyncFunction 被放入调用栈并执行,console.log('asyncFunction start') 被执行。
  5. await Promise.resolve() 被放入 microtask queue,asyncFunction 暂停。
  6. console.log('script end') 被放入调用栈并执行。
  7. 调用栈清空后,event loop 检查 microtask queue 并执行:
    • Promise.resolve().then 中的回调,console.log('promise1')
    • console.log('promise2')
    • await Promise.resolve() 之后的代码,console.log('asyncFunction end')
  8. 最后,event loop 检查 macrotask queue 并执行 setTimeout 回调。

最终输出顺序是:

script start
asyncFunction start
script end
promise1
promise2
asyncFunction end
setTimeout

内部工作原理

Promise 的内部机制

当创建一个 Promise 时,构造函数内的代码会立即执行。但 then 和 catch 回调会被放入 microtask queue,等待当前事件循环结束后执行。

const promise = new Promise((resolve, reject) => {
  console.log('Promise executor');
  resolve();
});

promise.then(() => {
  console.log('Promise then');
});

console.log('End');

执行顺序:

  1. console.log('Promise executor')
  2. resolve 被调用,将 then 回调放入 microtask queue。
  3. console.log('End')
  4. microtask queue 被处理,执行 then 回调,console.log('Promise then')

最终输出:

Promise executor
End
Promise then
async/await 的内部机制

async 函数会返回一个 Promise,await 表达式会暂停 async 函数的执行,等待 Promise 解决后继续执行。await 表达式相当于 Promise 的 then 方法,因此它的回调也会放入 microtask queue。

async function asyncFunction() {
  console.log('asyncFunction start');
  await Promise.resolve();
  console.log('asyncFunction end');
}

asyncFunction();
console.log('End');

执行顺序:

  1. console.log('asyncFunction start')
  2. await Promise.resolve() 将回调放入 microtask queue,暂停 asyncFunction。
  3. console.log('End')
  4. microtask queue 被处理,执行 await 之后的代码,console.log('asyncFunction end')

最终输出:

asyncFunction start
End
asyncFunction end

微任务和宏任务

微任务(microtask)和宏任务(macrotask)的区别在于它们在 event loop 中的执行时机。微任务包括 Promise 的 then/catch/finally 回调和 MutationObserver 回调。宏任务包括 setTimeout、setInterval、setImmediate、I/O 事件和 UI 渲染。

每次事件循环,首先处理所有微任务队列中的任务,然后处理一个宏任务。

示例:
console.log('script start');

setTimeout(() => {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(() => {
  console.log('promise1');
}).then(() => {
  console.log('promise2');
});

console.log('script end');

执行顺序:

  1. console.log('script start')
  2. setTimeout 回调放入宏任务队列。
  3. Promise.resolve().then 回调放入微任务队列。
  4. console.log('script end')
  5. 处理微任务队列,执行 promise1promise2
  6. 处理宏任务队列,执行 setTimeout 回调。

最终输出:

script start
script end
promise1
promise2
setTimeout

实践应用

了解 Promise 和 async/await 在 event loop 中的调用机制对编写高效、可维护的异步代码非常重要。合理使用 microtask 和 macrotask,可以优化代码执行顺序,避免不必要的延迟。

示例:顺序执行多个异步操作
function fetchData(url) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`Data from ${url}`);
    }, 1000);
  });
}

async function fetchSequentially() {
  console.log('Start fetching');

  const data1 = await fetchData('url1');
  console.log(data1);

  const data2 = await fetchData('url2');
  console.log(data2);

  console.log('Finished fetching');
}

fetchSequentially();

在这个例子中,fetchSequentially 函数顺序执行两个异步操作,确保在第一个操作完成后才开始第二个操作。

最终输出:

Start

 fetching
Data from url1
Data from url2
Finished fetching

结论

理解 Promise 和 async/await 在 event loop 中的调用机制,可以帮助开发者编写更高效和可维护的异步代码。通过掌握微任务和宏任务的执行时机,开发者可以优化代码执行顺序,避免不必要的延迟,提高应用性能。这个知识不仅适用于 JavaScript,也适用于其他基于事件驱动模型的编程语言和环境。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
promiseasync/await都是用于处理异步操作的方式,但它们在语法和使用上有一些区别。 1. PromisePromise是ES6引入的一种处理异步操作的方式。它是一个对象,表示一个异步操作的最终完成(或失败)及其结果的值。Promise提供了一个链式的调用方式,可以通过then()方法来处理异步操作的结果,也可以通过catch()方法来处理异常情况。 2. async/awaitasync/await是ES8引入的一种处理异步操作的方式。它是基于Promise的语法糖,使得异步代码看起来更像同步代码。使用async关键字声明一个函数为异步函数,然后在需要等待异步操作结果的地方使用await关键字来等待Promise对象的完成。 区别: - 语法简洁性:async/await相对于Promise更加简洁,更接近于同步代码的写法,使得代码更易读、易理解。 - 错误处理:使用Promise时需要通过catch()方法来捕获异常,而async/await可以使用try/catch语句来处理异常,使得错误处理更加直观。 - 链式调用Promise通过链式调用的方式来处理多个异步操作,而async/await可以使用同步的方式编写多个异步操作,使得逻辑更加清晰。 - 可读性:async/await相对于Promise更加易于理解和阅读,特别是对于有较多异步操作的代码块。 需要注意的是,async/await是基于Promise的,实际上async函数会返回一个Promise对象。在使用async/await时,可以将任何返回Promise对象的异步操作转化为同步的写法,但在某些情况下可能会导致阻塞。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值