Promise的诞生与本质

本文最后更新于:2025年9月4日 下午

Promise的诞生与本质

大家都说Promise是用来解决回调地狱(callback hell)的,那回调地狱到底是什么?

setTimeout(() => {
  console.log('first level');
  setTimeout(() => {
    console.log('second level');
    setTimeout(() => {
      console.log('third level');
    }, 1000);
  }, 1000);
}, 1000);

由于异步任务需要等待完成再进行处理(执行回调函数),那么如果另一个异步任务依赖于前一个异步任务,那么该异步任务就需要嵌套进回调函数中。多个依赖关系就会导致层层嵌套,最终形成一个长长的回调地狱(丢失可读性和可维护性)

Promise提供了一种机制从回调函数提取出关键数据,从而使用链式调用的形式在后续进行数据处理,避免直接在回调函数中处理。这样就从【嵌套】变成了【平铺】

new Promise(resolve => {
  setTimeout(() => {
    resolve('first level');
  }, 1000);
})
  .then(result => {
    console.log(result);
    return new Promise(resolve => {
      setTimeout(() => {
        resolve('second level');
      }, 1000);
    });
  })
  .then(result => {
    console.log(result);
    return new Promise(resolve => {
      setTimeout(() => {
        resolve('third level');
      }, 1000);
    });
  })
  .then(result => {
    console.log(result);
  })

⚠️注意:Promise只是提供了一种代码风格,他本身和异步没有任何关系!!!

解释:因为现在很多异步任务为了避免回调地狱都封装了一层Promise,所以很多人将Promise和异步挂钩,实际上Promise只是做了代码风格调整。

题外内容——事件循环

在面试中,经常会将Promise内容和事件循环联系起来,考察代码执行的顺序。这里考察的点实际上就是.then()中的回调函数是微任务,需要放到微队列中,而且微队列的优先级是最高的

setTimeout(() => {
  console.log('延时任务');
}, 0);
document.addEventListener('DOMContentLoaded', function() {
  console.log('交互任务');
})
Promise.resolve('微任务').then(result => console.log(result));
console.log('渲染主线程');
/*
顺序代表了每种任务的优先级,正确的打印顺序应该是(需开启live-server):
渲染主线程
微任务
Live reload enabled.
交互任务
延时任务
*/

需要注意的是Promise.resolve().then()的方式和new Promise(resolve => {}).then()方式虽然都会产生微任务,但是new Promise(resolve => {})渲染主线程会直接执行,所以需要稍微注意一下:

Promise.resolve('微任务1').then(result => console.log(result));
new Promise(resolve => {
  console.log('渲染主线程'); // 同步执行
  setTimeout(() => {
    resolve('微任务2');
  }, 0);
}).then(result => console.log(result));
/*
顺序是:
渲染主线程
微任务1
微任务2
*/

最终形态

随着async/await出现,现在异步任务的时候也不再使用Promise的链式了,而是回归到了以前同步代码的写法。

整个发展历程就是:【嵌套】 -> 【链式】 -> 【同步】

从处理方式通俗来说就是:处理代码从写在回调函数中,变成写到链式的回调中,最后直接写到了下一行代码就行

const asyncTask = (msg) => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(msg);
    }, 1000);
  })
};

const res1 = await asyncTask('first level');
console.log(res1);
const res2 = await asyncTask('second level');
console.log(res2);
const res3 = await asyncTask('third level');
console.log(res3);

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!