前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • Vue

  • React

  • Webpack

  • Node

  • Javascript

  • 综合

    • 虚拟DOM原理分析
    • setTimeout实现原理和使用注意
    • 浅析Promise原理
    • 浏览器渲染原理
    • 前端面试之MVVM浅析
    • 前端面试之组件化
    • 虚拟DOM(一)
    • 虚拟DOM(二)
    • 前端性能之Performance
    • 小程序开发实践
    • 对比 Koa 和 Redux-分析前端中的中间件理念
    • 正则完整篇
    • 打造前端监控系统
    • 浏览器渲染机制
    • 前端缓存方案解析
    • 小程序原理
    • 深入剖析浏览器中页面的渲染过程
    • 一个网络请求是怎么进行的
    • 前端性能定位

文章学习导航
面试总览

30 秒速记

  • 承诺保存一次异步结果,落定后不能反转
  • 执行器同步运行,反应回调异步安排
  • then 返回新承诺,承接值、异常或其他承诺
  • 返回内层任务才能把它接进链
  • 聚合失败不等于取消其他底层任务

Promise 像一张结果收据,先拿到收据,等工作结束后再读取成功值或失败原因。 执行器创建时同步执行,then 回调则异步运行;每次链式调用产生新承诺,回调返回值、抛错或返回的异步任务决定下一步结果。已经落定的状态不能翻转,但用另一个承诺解析时可能仍在等待它。写链条最容易漏掉 return,让内层任务脱离整体错误处理。

版本校准: 原理文章中的代码代表特定实现与写作时间。应用到当前项目时,应先确认浏览器、框架或工具的主版本,再区分稳定的规范语义、可变化的内部实现和项目自身约束。

# 一、Promise基础用法

# 1.1 基本用法

new Promise(function(resolve, reject) {
    //待处理的异步逻辑
    //处理结束后,调用resolve或reject方法
})
@前端进阶之旅: 代码已经复制到剪贴板
  • 新建一个promise很简单,只需要new一个promise对象即可。其中 Promise 是构造函数,创建出来的承诺是对象,它接受一个函数作为参数,并且会返回promise对象,这就给链式调用提供了基础
  • 其实Promise函数的使命,就是构建出它的实例,并且负责帮我们管理这些实例。而这些实例有以下三种状态:
  • pending: 初始状态,未兑现或拒绝
  • fulfilled: 意味着操作成功完成
  • rejected: 意味着操作失败

pending 状态的 Promise对象可能以 fulfilled状态返回了一个值,也可能被某种理由(异常信息)拒绝(reject)了。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers)就会被调用,then方法分别指定了resolve方法和reject方法的回调函数

img

var promise = new Promise(function(resolve, reject) {
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
promise.then(function(value) {
  // 如果调用了resolve方法,执行此函数
}, function(value) {
  // 如果调用了reject方法,执行此函数
});
@前端进阶之旅: 代码已经复制到剪贴板

上述代码很清晰的展示了promise对象运行的机制。下面再看一个示例:

function getJSON(url) {
  return new Promise((resolve, reject) => {
    const client = new XMLHttpRequest()
    client.open('GET', url)
    client.responseType = 'json'
    client.timeout = 10000
    client.setRequestHeader('Accept', 'application/json')
    client.onload = () => {
      if (client.status >= 200 && client.status < 300) resolve(client.response)
      else reject(new Error('HTTP ' + client.status))
    }
    client.onerror = () => reject(new Error('Network error'))
    client.ontimeout = () => reject(new Error('Request timed out'))
    client.onabort = () => reject(new Error('Request aborted'))
    client.send()
  })
}
getJSON("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出错了', error);
});
@前端进阶之旅: 代码已经复制到剪贴板

上面代码中,resolve方法和reject方法调用时,都带有参数。它们的参数会被传递给回调函数。reject方法的参数通常是Error对象的实例,而resolve方法的参数除了正常的值以外,还可能是另一个Promise实例,比如像下面这样。

var p1 = new Promise(function(resolve, reject){
  // ... some code
});
var p2 = new Promise(function(resolve, reject){
  // ... some code
  resolve(p1);
})
@前端进阶之旅: 代码已经复制到剪贴板

上面代码中,p1和p2都是Promise的实例,但是p2的resolve方法将p1作为参数,这时p1的状态就会传递给p2。如果调用的时候,p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是fulfilled或者rejected,那么 p2 会跟随它的结果,已注册的反应回调仍要异步调度,不会在当前同步调用栈中立刻执行

# 1.2 promise捕获错误

Promise.prototype.catch方法是Promise.prototype.then(null, rejection)的别名,用于指定发生错误时的回调函数

getJSON("/visa.json").then(function(result) {
  // some code
}).catch(function(error) {
  // 处理前一个回调函数运行时发生的错误
  console.log('出错啦!', error);
});
@前端进阶之旅: 代码已经复制到剪贴板

Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获

getJSON("/visa.json").then(function(json) {
  return json.name;
}).then(function(name) {
  // proceed
}).catch(function(error) {
    //处理前面任一个then函数抛出的错误
});
@前端进阶之旅: 代码已经复制到剪贴板

# 1.3 常用的promise方法

Promise.all方法

Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例

var p = Promise.all([p1,p2,p3]);
@前端进阶之旅: 代码已经复制到剪贴板
  • Promise.all 接受可迭代输入,成员可以是普通值、承诺或可同化对象,会按 Promise.resolve 的语义处理。结果保持输入顺序,空输入也有明确的成功结果。

p的状态由p1、p2、p3决定,分成两种情况

  • 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数
  • 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数
// 生成一个Promise对象的数组
var promises = [2, 3, 5, 7, 11, 13].map(function(id){
  return getJSON("/get/addr" + id + ".json");
});
Promise.all(promises).then(function(posts) {
  // ...  
}).catch(function(reason){
  // ...
});
@前端进阶之旅: 代码已经复制到剪贴板

Promise.race方法

Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例。

var p = Promise.race([p1,p2,p3]);
@前端进阶之旅: 代码已经复制到剪贴板

上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值

  • 如果Promise.all方法和Promise.race方法的参数,不是Promise实例,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理

Promise.resolve

有时需要将现有对象转为Promise对象,Promise.resolve方法就起到这个作用

var jsPromise = Promise.resolve($.ajax('/whatever.json'));
@前端进阶之旅: 代码已经复制到剪贴板

上面代码将jQuery生成deferred对象,转为一个新的ES6的Promise对象

  • 如果Promise.resolve方法的参数,不是具有then方法的对象(又称thenable对象),则返回一个新的Promise对象,且它的状态为fulfilled。
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • Vue

  • React

  • Webpack

  • Node

  • Javascript

  • 综合

    • 虚拟DOM原理分析
    • setTimeout实现原理和使用注意
    • 浅析Promise原理
      • 一、Promise基础用法
        • 1.1 基本用法
        • 1.2 promise捕获错误
        • 1.3 常用的promise方法
        • 1.4 Async/await简化写法
        • 1.5 Generator
      • 二、Promise实现原理剖析
    • 浏览器渲染原理
    • 前端面试之MVVM浅析
    • 前端面试之组件化
    • 虚拟DOM(一)
    • 虚拟DOM(二)
    • 前端性能之Performance
    • 小程序开发实践
    • 对比 Koa 和 Redux-分析前端中的中间件理念
    • 正则完整篇
    • 打造前端监控系统
    • 浏览器渲染机制
    • 前端缓存方案解析
    • 小程序原理
    • 深入剖析浏览器中页面的渲染过程
    • 一个网络请求是怎么进行的
    • 前端性能定位
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

浅析Promise原理|原理篇

文章学习导航
面试总览

30 秒速记

  • 承诺保存一次异步结果,落定后不能反转
  • 执行器同步运行,反应回调异步安排
  • then 返回新承诺,承接值、异常或其他承诺
  • 返回内层任务才能把它接进链
  • 聚合失败不等于取消其他底层任务

Promise 像一张结果收据,先拿到收据,等工作结束后再读取成功值或失败原因。 执行器创建时同步执行,then 回调则异步运行;每次链式调用产生新承诺,回调返回值、抛错或返回的异步任务决定下一步结果。已经落定的状态不能翻转,但用另一个承诺解析时可能仍在等待它。写链条最容易漏掉 return,让内层任务脱离整体错误处理。

🧪​可交互​动手试试

版本校准: 原理文章中的代码代表特定实现与写作时间。应用到当前项目时,应先确认浏览器、框架或工具的主版本,再区分稳定的规范语义、可变化的内部实现和项目自身约束。

# 一、Promise基础用法

# 1.1 基本用法

new Promise(function(resolve, reject) {
    //待处理的异步逻辑
    //处理结束后,调用resolve或reject方法
})
@前端进阶之旅: 代码已经复制到剪贴板
  • 新建一个promise很简单,只需要new一个promise对象即可。其中 Promise 是构造函数,创建出来的承诺是对象,它接受一个函数作为参数,并且会返回promise对象,这就给链式调用提供了基础
  • 其实Promise函数的使命,就是构建出它的实例,并且负责帮我们管理这些实例。而这些实例有以下三种状态:
  • pending: 初始状态,未兑现或拒绝
  • fulfilled: 意味着操作成功完成
  • rejected: 意味着操作失败

pending 状态的 Promise对象可能以 fulfilled状态返回了一个值,也可能被某种理由(异常信息)拒绝(reject)了。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers)就会被调用,then方法分别指定了resolve方法和reject方法的回调函数

img

var promise = new Promise(function(resolve, reject) {
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
promise.then(function(value) {
  // 如果调用了resolve方法,执行此函数
}, function(value) {
  // 如果调用了reject方法,执行此函数
});
@前端进阶之旅: 代码已经复制到剪贴板

上述代码很清晰的展示了promise对象运行的机制。下面再看一个示例:

function getJSON(url) {
  return new Promise((resolve, reject) => {
    const client = new XMLHttpRequest()
    client.open('GET', url)
    client.responseType = 'json'
    client.timeout = 10000
    client.setRequestHeader('Accept', 'application/json')
    client.onload = () => {
      if (client.status >= 200 && client.status < 300) resolve(client.response)
      else reject(new Error('HTTP ' + client.status))
    }
    client.onerror = () => reject(new Error('Network error'))
    client.ontimeout = () => reject(new Error('Request timed out'))
    client.onabort = () => reject(new Error('Request aborted'))
    client.send()
  })
}
getJSON("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出错了', error);
});
@前端进阶之旅: 代码已经复制到剪贴板

上面代码中,resolve方法和reject方法调用时,都带有参数。它们的参数会被传递给回调函数。reject方法的参数通常是Error对象的实例,而resolve方法的参数除了正常的值以外,还可能是另一个Promise实例,比如像下面这样。

var p1 = new Promise(function(resolve, reject){
  // ... some code
});
var p2 = new Promise(function(resolve, reject){
  // ... some code
  resolve(p1);
})
@前端进阶之旅: 代码已经复制到剪贴板

上面代码中,p1和p2都是Promise的实例,但是p2的resolve方法将p1作为参数,这时p1的状态就会传递给p2。如果调用的时候,p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是fulfilled或者rejected,那么 p2 会跟随它的结果,已注册的反应回调仍要异步调度,不会在当前同步调用栈中立刻执行

# 1.2 promise捕获错误

Promise.prototype.catch方法是Promise.prototype.then(null, rejection)的别名,用于指定发生错误时的回调函数

getJSON("/visa.json").then(function(result) {
  // some code
}).catch(function(error) {
  // 处理前一个回调函数运行时发生的错误
  console.log('出错啦!', error);
});
@前端进阶之旅: 代码已经复制到剪贴板

Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获

getJSON("/visa.json").then(function(json) {
  return json.name;
}).then(function(name) {
  // proceed
}).catch(function(error) {
    //处理前面任一个then函数抛出的错误
});
@前端进阶之旅: 代码已经复制到剪贴板

# 1.3 常用的promise方法

Promise.all方法

Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例

var p = Promise.all([p1,p2,p3]);
@前端进阶之旅: 代码已经复制到剪贴板
  • Promise.all 接受可迭代输入,成员可以是普通值、承诺或可同化对象,会按 Promise.resolve 的语义处理。结果保持输入顺序,空输入也有明确的成功结果。

p的状态由p1、p2、p3决定,分成两种情况

  • 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数
  • 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数
// 生成一个Promise对象的数组
var promises = [2, 3, 5, 7, 11, 13].map(function(id){
  return getJSON("/get/addr" + id + ".json");
});
Promise.all(promises).then(function(posts) {
  // ...  
}).catch(function(reason){
  // ...
});
@前端进阶之旅: 代码已经复制到剪贴板

Promise.race方法

Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例。

var p = Promise.race([p1,p2,p3]);
@前端进阶之旅: 代码已经复制到剪贴板

上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值

  • 如果Promise.all方法和Promise.race方法的参数,不是Promise实例,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理

Promise.resolve

有时需要将现有对象转为Promise对象,Promise.resolve方法就起到这个作用

var jsPromise = Promise.resolve($.ajax('/whatever.json'));
@前端进阶之旅: 代码已经复制到剪贴板

上面代码将jQuery生成deferred对象,转为一个新的ES6的Promise对象

  • 如果Promise.resolve方法的参数,不是具有then方法的对象(又称thenable对象),则返回一个新的Promise对象,且它的状态为fulfilled。
var p = Promise.resolve('Hello');
p.then(function (s){
  console.log(s)
});
// Hello
@前端进阶之旅: 代码已经复制到剪贴板
  • 上面代码生成一个新的Promise对象的实例p,它的状态为fulfilled,但 then 回调仍异步执行,Promise.resolve方法的参数就是回调函数的参数
  • 对普通的 Promise.resolve(p),当 p 是构造器匹配的承诺时可以直接返回;子类或不同构造器的实例不能一概套用这个身份结论。
  • Promise.reject(reason)方法也会返回一个新的Promise实例,该实例的状态为rejected。Promise.reject方法的参数reason,会被传递给实例的回调函数
var p = Promise.reject('出错啦');
p.then(null, function (error){
  console.log(error)
});
// 出错了
@前端进阶之旅: 代码已经复制到剪贴板

# 1.4 Async/await简化写法

function getDataAsync (url) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            var res = {
                url: url,
                data: Math.random()
            }
            resolve(res)
        }, 1000)
    })
}
async function getData () {
    var res1 = await getDataAsync('/page/1?param=123')
    console.log(res1)
    var res2 = await getDataAsync(`/page/2?param=${res1.data}`)
    console.log(res2)
    var res3 = await getDataAsync(`/page/2?param=${res2.data}`)
    console.log(res3)
}
@前端进阶之旅: 代码已经复制到剪贴板

← setTimeout实现原理和使用注意浏览器渲染原理 →

fe