MST

星途 面试题库

面试题:网络编程中JavaScript的Promise在后端开发的应用场景

在后端开发涉及网络请求的场景下,比如调用外部API获取数据,说明Promise如何管理异步操作,以及它相较于传统回调函数的优势有哪些?请举例说明。
28.5万 热度难度
后端开发网络编程

知识考点

AI 面试

面试题答案

一键面试

Promise管理异步操作方式

  1. 创建Promise对象:通过new Promise((resolve, reject) => { /* 异步操作代码 */ })来创建一个Promise实例。在这个构造函数中,传入一个执行器函数,该函数接收两个参数resolvereject。异步操作(如网络请求)在这个执行器函数内执行。如果异步操作成功,调用resolve并传入成功的数据;如果失败,调用reject并传入错误信息。
  2. 链式调用:Promise实例有.then().catch()等方法。.then()方法接收两个回调函数作为参数,第一个回调函数在Promise被resolve时调用,第二个回调函数(可选)在Promise被reject时调用。可以通过链式调用多个.then()方法,每个.then()返回一个新的Promise,这样可以按顺序处理异步操作的结果。例如:
fetch('https://example.com/api/data')
  .then(response => response.json())
  .then(data => {
      console.log(data);
      return anotherAsyncOperation(data);
    })
  .then(result => {
      console.log(result);
    })
  .catch(error => {
      console.error('Error:', error);
    });
  1. async/await语法糖:基于Promise,async函数可以更简洁地处理异步操作。async函数内部可以使用await关键字暂停函数执行,等待Promise被解决(resolved或rejected)。await只能在async函数内部使用。例如:
async function getData() {
  try {
    const response = await fetch('https://example.com/api/data');
    const data = await response.json();
    console.log(data);
    const result = await anotherAsyncOperation(data);
    console.log(result);
  } catch (error) {
    console.error('Error:', error);
  }
}

相较于传统回调函数的优势

  1. 解决回调地狱问题:传统回调函数在处理多个异步操作且存在依赖关系时,会出现层层嵌套的情况,代码可读性和维护性变差,即所谓的“回调地狱”。而Promise通过链式调用,将异步操作以更线性的方式呈现。例如:
// 回调地狱示例
fs.readFile('file1.txt', 'utf8', (err1, data1) => {
  if (err1) {
    console.error(err1);
    return;
  }
  fs.readFile('file2.txt', 'utf8', (err2, data2) => {
    if (err2) {
      console.error(err2);
      return;
    }
    fs.readFile('file3.txt', 'utf8', (err3, data3) => {
      if (err3) {
        console.error(err3);
        return;
      }
      console.log(data1, data2, data3);
    });
  });
});

// 使用Promise改写
const fs = require('fs').promises;
fs.readFile('file1.txt', 'utf8')
  .then(data1 => {
      return fs.readFile('file2.txt', 'utf8').then(data2 => {
        return fs.readFile('file3.txt', 'utf8').then(data3 => {
          console.log(data1, data2, data3);
        });
      });
    })
  .catch(error => {
      console.error(error);
    });
  1. 错误处理更统一:在Promise中,通过.catch()方法可以捕获整个链式调用中任何一个Promise被reject的错误,而不需要在每个回调函数内部单独处理错误。在传统回调函数中,每个回调都需要手动检查错误。例如:
// Promise错误处理
fetch('https://nonexistent-url.com/api/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

// 传统回调错误处理
function makeRequest(callback) {
  // 模拟网络请求
  setTimeout(() => {
    const hasError = true;
    if (hasError) {
      callback(new Error('Request failed'));
    } else {
      callback(null, 'Data');
    }
  }, 1000);
}

makeRequest((error, data) => {
  if (error) {
    console.error('Error:', error);
  } else {
    console.log(data);
  }
});
  1. 更好的代码组织和复用:Promise可以将异步操作封装成独立的函数并返回Promise实例,方便在不同地方复用。例如,将获取外部API数据封装成一个函数:
function fetchData() {
  return fetch('https://example.com/api/data')
    .then(response => response.json());
}

// 复用
fetchData()
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));