# JavaScript中如何巧妙等待回调函数执行完成

在JavaScript中,异步编程是必不可少的技能,特别是在处理I/O操作或者长时间运行的任务时。回调函数作为一种简单的异步编程手段,虽然容易实现,但如果不恰当使用,可能会导致代码难以维护和理解。下面,我们将探讨几种巧妙的方法来等待回调函数执行完成。

## 使用回调函数

最简单的方法就是直接在回调函数中使用。这种方式在JavaScript中非常常见:

```javascript
function asyncOperation(callback) {
  // 执行一些异步操作
  setTimeout(() => {
    callback(null, '操作结果');
  }, 1000);
}

asyncOperation((err, result) => {
  if (err) {
    console.error('操作出错:', err);
  } else {
    console.log('操作完成:', result);
  }
});

这种方法的缺点是回调地狱,即多个回调函数嵌套,使得代码结构复杂,可读性差。

使用Promise

Promise是ES6引入的一个更加强大和易于理解的异步编程模型。使用Promise可以避免回调地狱,使得异步代码更加清晰。

function asyncOperation() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // 假设操作成功
      resolve('操作结果');
      // 如果操作失败,可以调用reject()
      // reject(new Error('操作出错'));
    }, 1000);
  });
}

asyncOperation().then(result => {
  console.log('操作完成:', result);
}).catch(err => {
  console.error('操作出错:', err);
});

使用async/await

ES2017引入的async/await语法使得异步代码看起来更像同步代码,极大地提高了代码的可读性。

async function asyncOperation() {
  try {
    const result = await asyncOperation();
    console.log('操作完成:', result);
  } catch (err) {
    console.error('操作出错:', err);
  }
}

asyncOperation();

在这个例子中,await关键字会等待asyncOperation()函数内部的异步操作完成。

使用Promise.all

当你有多个异步操作需要同时执行,并且需要等待它们全部完成时,可以使用Promise.all

function asyncOperation1() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('结果1');
    }, 1000);
  });
}

function asyncOperation2() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('结果2');
    }, 1000);
  });
}

Promise.all([asyncOperation1(), asyncOperation2()])
  .then(results => {
    console.log('操作1和操作2都完成了:', results);
  });

总结

JavaScript提供了多种方式来等待回调函数执行完成,从传统的回调函数到Promise和async/await。选择哪种方法取决于具体的需求和项目的上下文。正确使用这些方法可以使得你的代码更加清晰、易于维护,并避免回调地狱。