Skip to content

Promise

Promise是ES6引入的一种处理异步操作的方式,它提供了一种更优雅、更灵活的方式来处理异步代码,避免了传统回调函数嵌套导致的"回调地狱"问题。Promise已经成为现代JavaScript中处理异步操作的标准方式,也是面试中经常被问到的话题。

Promise的基本概念

什么是Promise?

Promise是一个对象,它代表了一个异步操作的最终完成(或失败)及其结果值。Promise有三种状态:

  1. pending:初始状态,既不是成功,也不是失败
  2. fulfilled:操作成功完成
  3. rejected:操作失败

当Promise的状态从pending变为fulfilled或rejected时,会触发相应的回调函数。一旦Promise的状态发生变化,就不会再改变。

Promise的创建

使用new Promise()构造函数创建一个新的Promise对象,它接受一个函数作为参数,该函数有两个参数:resolvereject,分别用于将Promise的状态变为fulfilled和rejected。

javascript
const promise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve('Operation successful');
    } else {
      reject('Operation failed');
    }
  }, 1000);
});

promise
  .then(result => {
    console.log(result); // 输出: Operation successful
  })
  .catch(error => {
    console.log(error); // 输出: Operation failed
  });

Promise的方法

then()方法

then()方法用于处理Promise成功时的回调,它接受两个参数:

  1. onFulfilled:Promise状态变为fulfilled时的回调函数
  2. onRejected:Promise状态变为rejected时的回调函数(可选)

then()方法返回一个新的Promise对象,因此可以链式调用。

javascript
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(10);
  }, 1000);
});

promise
  .then(result => {
    console.log(result); // 输出: 10
    return result * 2;
  })
  .then(result => {
    console.log(result); // 输出: 20
    return result * 3;
  })
  .then(result => {
    console.log(result); // 输出: 60
  });

catch()方法

catch()方法用于处理Promise失败时的回调,它接受一个参数:

  1. onRejected:Promise状态变为rejected时的回调函数

catch()方法返回一个新的Promise对象,因此可以链式调用。

javascript
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('Error occurred');
  }, 1000);
});

promise
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.log(error); // 输出: Error occurred
  });

finally()方法

finally()方法用于处理Promise完成(无论成功还是失败)后的回调,它接受一个参数:

  1. onFinally:Promise完成后的回调函数

finally()方法返回一个新的Promise对象,因此可以链式调用。

javascript
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Operation successful');
  }, 1000);
});

promise
  .then(result => {
    console.log(result); // 输出: Operation successful
  })
  .catch(error => {
    console.log(error);
  })
  .finally(() => {
    console.log('Operation completed'); // 输出: Operation completed
  });

Promise.all()方法

Promise.all()方法用于处理多个Promise,它接受一个Promise数组作为参数,返回一个新的Promise对象:

  • 当所有Promise都成功时,返回的Promise状态变为fulfilled,其值是所有Promise结果的数组
  • 当任一Promise失败时,返回的Promise状态变为rejected,其值是第一个失败的Promise的错误
javascript
const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Promise 1 successful');
  }, 1000);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Promise 2 successful');
  }, 2000);
});

const promise3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Promise 3 successful');
  }, 1500);
});

Promise.all([promise1, promise2, promise3])
  .then(results => {
    console.log(results); // 输出: ['Promise 1 successful', 'Promise 2 successful', 'Promise 3 successful']
  })
  .catch(error => {
    console.log(error);
  });

Promise.race()方法

Promise.race()方法用于处理多个Promise,它接受一个Promise数组作为参数,返回一个新的Promise对象:

  • 当任一Promise完成(成功或失败)时,返回的Promise状态变为与该Promise相同的状态,其值是该Promise的结果
javascript
const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Promise 1 successful');
  }, 1000);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Promise 2 successful');
  }, 2000);
});

Promise.race([promise1, promise2])
  .then(result => {
    console.log(result); // 输出: Promise 1 successful
  })
  .catch(error => {
    console.log(error);
  });

Promise.allSettled()方法

Promise.allSettled()方法用于处理多个Promise,它接受一个Promise数组作为参数,返回一个新的Promise对象:

  • 当所有Promise都完成(成功或失败)时,返回的Promise状态变为fulfilled,其值是一个数组,每个元素包含对应Promise的状态和结果
javascript
const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Promise 1 successful');
  }, 1000);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('Promise 2 failed');
  }, 2000);
});

Promise.allSettled([promise1, promise2])
  .then(results => {
    console.log(results);
    // 输出:
    // [
    //   { status: 'fulfilled', value: 'Promise 1 successful' },
    //   { status: 'rejected', reason: 'Promise 2 failed' }
    // ]
  });

Promise.resolve()方法

Promise.resolve()方法返回一个状态为fulfilled的Promise对象,其值是传入的参数。

javascript
const promise = Promise.resolve('Operation successful');

promise.then(result => {
  console.log(result); // 输出: Operation successful
});

Promise.reject()方法

Promise.reject()方法返回一个状态为rejected的Promise对象,其值是传入的参数。

javascript
const promise = Promise.reject('Operation failed');

promise.catch(error => {
  console.log(error); // 输出: Operation failed
});

Promise的应用场景

1. 处理异步操作

Promise最基本的应用场景是处理异步操作,如网络请求、文件读写等:

javascript
// 网络请求
function fetchData(url) {
  return new Promise((resolve, reject) => {
    fetch(url)
      .then(response => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.json();
      })
      .then(data => {
        resolve(data);
      })
      .catch(error => {
        reject(error);
      });
  });
}

fetchData('https://api.example.com/data')
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.log(error);
  });

// 文件读写
const fs = require('fs');

function readFile(path) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, 'utf8', (error, data) => {
      if (error) {
        reject(error);
      } else {
        resolve(data);
      }
    });
  });
}

readFile('example.txt')
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.log(error);
  });

2. 链式调用

Promise的链式调用使得异步代码更加清晰易读,避免了回调地狱:

javascript
// 传统回调地狱
function fetchData(callback) {
  fetch('https://api.example.com/data')
    .then(response => response.json())
    .then(data => {
      fetch(`https://api.example.com/users/${data.userId}`)
        .then(response => response.json())
        .then(user => {
          fetch(`https://api.example.com/posts?userId=${user.id}`)
            .then(response => response.json())
            .then(posts => {
              callback(posts);
            });
        });
    });
}

// Promise链式调用
function fetchData() {
  return fetch('https://api.example.com/data')
    .then(response => response.json())
    .then(data => {
      return fetch(`https://api.example.com/users/${data.userId}`);
    })
    .then(response => response.json())
    .then(user => {
      return fetch(`https://api.example.com/posts?userId=${user.id}`);
    })
    .then(response => response.json());
}

fetchData()
  .then(posts => {
    console.log(posts);
  })
  .catch(error => {
    console.log(error);
  });

3. 并行操作

使用Promise.all()方法可以并行执行多个异步操作,提高效率:

javascript
function fetchUser(id) {
  return fetch(`https://api.example.com/users/${id}`)
    .then(response => response.json());
}

function fetchPosts(userId) {
  return fetch(`https://api.example.com/posts?userId=${userId}`)
    .then(response => response.json());
}

function fetchComments(postId) {
  return fetch(`https://api.example.com/comments?postId=${postId}`)
    .then(response => response.json());
}

const userId = 1;

Promise.all([
  fetchUser(userId),
  fetchPosts(userId),
  fetchComments(1)
])
  .then(([user, posts, comments]) => {
    console.log('User:', user);
    console.log('Posts:', posts);
    console.log('Comments:', comments);
  })
  .catch(error => {
    console.log(error);
  });

Promise的优缺点

优点

  1. 避免回调地狱:Promise的链式调用使得异步代码更加清晰易读
  2. 更好的错误处理:使用catch()方法集中处理错误
  3. 更好的代码组织:Promise使得异步代码的结构更加清晰
  4. 更好的组合性:使用Promise.all()Promise.race()等方法可以组合多个异步操作

缺点

  1. 浏览器兼容性:虽然现代浏览器都支持Promise,但旧版本浏览器可能需要使用polyfill
  2. 内存占用:Promise链可能会导致内存占用增加
  3. 调试困难:Promise链的错误堆栈可能不够清晰
  4. 无法取消:一旦创建了Promise,就无法取消它

面试常见问题

1. 什么是Promise?Promise有哪些状态?

  • Promise的定义:Promise是一个对象,它代表了一个异步操作的最终完成(或失败)及其结果值
  • Promise的状态
    • pending:初始状态,既不是成功,也不是失败
    • fulfilled:操作成功完成
    • rejected:操作失败

2. Promise的链式调用是如何工作的?

  • then()方法返回一个新的Promise对象
  • 每个then()方法的回调函数可以返回一个值或另一个Promise
  • 如果返回的是一个值,下一个then()方法的回调函数会接收这个值
  • 如果返回的是一个Promise,下一个then()方法的回调函数会等待这个Promise完成,然后接收它的结果

3. Promise.all()和Promise.race()的区别是什么?

  • Promise.all():当所有Promise都成功时,返回的Promise状态变为fulfilled,其值是所有Promise结果的数组;当任一Promise失败时,返回的Promise状态变为rejected,其值是第一个失败的Promise的错误
  • Promise.race():当任一Promise完成(成功或失败)时,返回的Promise状态变为与该Promise相同的状态,其值是该Promise的结果

4. 如何实现Promise.all()方法?

javascript
function myPromiseAll(promises) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(promises)) {
      reject(new TypeError('Argument must be an array'));
    }

    const results = [];
    let completedCount = 0;
    const totalCount = promises.length;

    if (totalCount === 0) {
      resolve(results);
      return;
    }

    promises.forEach((promise, index) => {
      Promise.resolve(promise)
        .then(value => {
          results[index] = value;
          completedCount++;
          if (completedCount === totalCount) {
            resolve(results);
          }
        })
        .catch(error => {
          reject(error);
        });
    });
  });
}

5. Promise和async/await的关系是什么?

  • async/await是基于Promise的语法糖,它使得异步代码看起来更像同步代码
  • async函数返回一个Promise对象
  • await表达式会暂停async函数的执行,等待Promise完成,然后返回Promise的结果
  • await只能在async函数中使用

总结

Promise是ES6引入的一种处理异步操作的方式,它提供了一种更优雅、更灵活的方式来处理异步代码,避免了传统回调函数嵌套导致的"回调地狱"问题。Promise有三种状态:pending、fulfilled和rejected。当Promise的状态发生变化时,会触发相应的回调函数。Promise提供了一系列方法,如then()、catch()、finally()、Promise.all()、Promise.race()等,使得我们可以更方便地处理异步操作。Promise已经成为现代JavaScript中处理异步操作的标准方式,它在网络请求、文件读写、异步流程控制等场景下非常实用。

好好学习,天天向上