📜  并发节点反应 - Javascript (1)

📅  最后修改于: 2023-12-03 15:09:46.941000             🧑  作者: Mango

并发节点反应 - Javascript

在JavaScript中,我们通常使用单线程模式来运行我们的应用程序。这意味着我们的应用程序只能执行一个任务。如果我们需要同时执行多个任务,我们可以使用并发节点反应(Concurrency model)。

并发节点反应将多个同时运行的任务组成一个线程池,以充分利用CPU和系统资源。这种模型通过异步和回调函数来管理并发性。通过使用这些,我们可以避免阻塞I/O并增加应用程序的响应能力。

异步操作

JavaScript中有三种类型的异步操作:回调函数、Promise对象和async/await函数。

回调函数

回调函数是异步的基础。它们是在函数调用后异步执行的函数。当一个函数被调用并立即返回时,回调函数被传递给一个异步函数,并在异步函数执行完毕后被调用。

function getUsers(callback) {
  setTimeout(() => {
    callback([{ name: 'Alice' }, { name: 'Bob' }]);
  }, 1000);
}

getUsers(users => {
  console.log(users);
});

在上面的代码中,我们定义了一个异步函数getUsers,它使用setTimeout模拟了一个异步操作,并在1秒钟后返回一个用户数组。在回调函数中,我们将打印出这个用户数组。

Promise

Promise是一种异步编程模型,它对回调函数进行了封装,并可以更好地处理异步操作。

function getUsers() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve([{ name: 'Alice' }, { name: 'Bob' }]);
    }, 1000);
  });
}

getUsers()
  .then(users => {
    console.log(users);
  });

在上面的代码中,我们定义了一个异步函数getUsers,它返回一个Promise对象。在Promise中,我们使用setTimeout模拟了一个异步操作,并在1秒钟后返回一个用户数组。在then方法中,我们打印出这个用户数组。

async/await

async/await是ES2017中引入的异步编程模型。它结合了Promise和异步函数,使编写异步代码变得更加简单。

async function getUsers() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve([{ name: 'Alice' }, { name: 'Bob' }]);
    }, 1000);
  });
}

async function main() {
  const users = await getUsers();
  console.log(users);
}

main();

在上面的代码中,我们定义了一个异步函数getUsers,它返回一个Promise对象,使用setTimeout模拟了一个异步操作,并在1秒钟后返回一个用户数组。在主函数中,我们使用await等待getUsers函数完成后返回结果,并打印出用户数组。

并发性

因为JavaScript是单线程的,所以当需要执行多个任务时,我们需要使用并发。在JavaScript中,我们使用事件循环机制来管理并发,它使用队列和堆栈来处理任务。

队列

任务队列是一种先进先出(FIFO)的数据结构。当一个异步操作完成时,它会将回调函数添加到队列的末尾。事件循环会从队列的头部开始处理回调函数,以确保执行的顺序是正确的。

setTimeout(() => console.log('first'), 0);
setTimeout(() => console.log('second'), 0);
setTimeout(() => console.log('third'), 0);
console.log('fourth');

在上面的代码中,我们使用三个setTimeout函数,打印出'first','second'和'third',并且它们的延迟时间都是0。在打印出'fourth'后,事件循环会将这三个回调函数添加到队列中。由于事件循环的处理是同步的,所以它会按照添加到队列的顺序依次执行回调函数。

堆栈

任务堆栈是一种后进先出(LIFO)的数据结构。当一个函数被调用时,它会将自己添加到堆栈顶部,当它返回时,它会从堆栈中弹出。这个过程是同步的,如果有一个函数被阻塞了,它会导致整个堆栈被阻塞,直到它返回。

function foo() {
  console.log('foo start');
  bar();
  console.log('foo end');
}

function bar() {
  console.log('bar start');
  const sum = 0;
  for (let i = 0; i < 10000000000; i++) {
    sum += i;
  }
  console.log('bar end');
}

foo();

在上面的代码中,我们定义了两个函数foo和bar。在foo中调用bar函数,并且在bar函数中模拟了一个阻塞的操作,它需要计算一个很长的循环。在执行这个代码时,foo函数将首先被添加到任务堆栈中,并打印'foo start'。当它调用bar函数时,bar函数将被添加到堆栈顶部,并打印'bar start',然后它会执行阻塞操作直到完成,并打印'bar end',最后它将被pop出堆栈。当bar函数返回时,foo函数将继续执行,打印'foo end'并被pop出堆栈。

结论

在JavaScript中,我们可以使用回调函数、Promise和async/await来执行异步任务,并且通过事件循环机制来处理多个任务的并发性。虽然JavaScript是单线程的,但我们可以使用适当的技术来提高应用程序的响应能力。