一,前言
上一篇,完成了 Promise 源码学习的目录;
本篇,主要对 Promise 进行简单的概括介绍;
二,Promise 简介
- Promise 是一个类或构造函数,是 JS 原生提供的,通过实例化 Promise 完成预期的异步任务处理;
- Promise 是一个能够获取异步操作消息的对象;译为承诺,表示会在一段时间后返回处理结果;
- Promise 是一种异步编程的解决方案,被 ES6 列为正式规范,提供了原生 Promise 对象;
- Promise 能够接收一个异步请求,在提供的 then/catch 方法中拿到异步请求的处理结果;
- Promise 支持链式调用,通过链式调用的方式能够将异步请求的结果以同步的方式进行处理;
三,Promise 基本使用
使用 Promise 对异步操作进行包装:
// 创建
var promise = new Promise((resolve, reject)=>{
setTimeout(()=>{
if("异步请求成功"){
resolve('ok');
} else {
reject('error')
}
}, 2000);
});
// 使用
promise.then(data=>{
console.log(data);
}).catch(error=>{
console.log(error);
});
从示例中能够看出:
- Promise 的参数是一个回调函数,其中包含了两个参数:resolve 和 reject;
- 当异步操作成功时,通过调用 resolve 返回异步操作的成功结果;
- 当异步操作失败时,通过调用 reject 返回异步操作的失败结果;
- 异步操作成功时,进入 then 处理后续操作;
- 异步操作失败时,进入 catch 处理后续操作;(当前示例是这样的,当then函数的第二个回调定义时,也可以捕捉失败操作,这个机制后面会详细解释)
但实际上,除以上示例体现出来的部分,Promise 还具有更多特性,理解并掌握这些特性是用好 Promise 的基础;
四,Promise 和 callback 对比
callback 方式处理异步操作:
function async(callback){
setTimeout(function(){
callback("异步操作完成");
}, 1000);
}
async(function(data){
console.log(data);
});
Promise 和 callback 的本质区别,就是控制权反转;
-
在 callback 模式下,回调函数的执行控制权在封装层:
- 业务层将回调函数定义传递给封装层;
- 封装层在任务结束时执行了回调函数;
- 业务层并没有调用回调函数,甚至连调用的代码都看不到;
-
在 Promise 模式下,回调函数的执行控制权在业务层:
- 业务层并没有把回调函数直接传递给封装层(Promise 对象内部);
- 封装层在任务结束时,并不知道要执行的回调,只能通过 resolve 或 reject 通知到业务层,由业务层在 then() 或 reject() 中控制回调执行;
- 业务层不仅能看到回调函数的调用代码,还能对其进行修改;
五,Promise 的作用
Promise 可以说(主要)是为了解决程序异步处理而生的;
- Promise 不仅解决了异步回调的多层嵌套,即“回调地狱”问题;
- 更重要的,Promise 提供了更完整、更强大的异步解决方案;通过 Promise 构造函数上提供的方法,实现对 Promise 批量操作的支持,如:all、race、any、allSettled 等;
六,Promise 的重要性
目前,Promise 在前端领域的应用中,几乎是无处不在的,已经成为了前端开发中最重要的技能之一;
Promise 也是前端面试的高频考察点:考察方式多样化,考察内容可深可浅;同时,也能够关联出很多实际应用场景;因此,熟练地掌握 promise 是每个前端开发的必备能力;
七,Promise 的兼容性
1,兼容性介绍
Promise 属于 ES6 规范内容,浏览器支持情况可查看 Can I use 或 MDN:
- Chrome 浏览器 - 支持
- 360 浏览器 - 兼容模式下支持
- IE 内核浏览器 - 不支持
2,解决方案
bluebird.js
在项目中使用 Promsie 对象,可以使用第三方插件 bluebird.js;
bluebird 对 ES6 原生 Promise 进行封装,解决了浏览器兼容性问题;
es6-promise
使用 es6-promise 也可以解决 IE 下不支持 Promise 的问题;
八,Promise 使用场景
JS 的异步处理可以说是无处不在的,比如:nodejs 和小程序所提供的 API 都是基于回调的;
而这也就导致了开发中时常出现“回调地狱”的坏味道(团队 CodeReview 关注点):
const fs = require('fs');
fs.readFile('./a.txt','utf8',(err,data)=>{
if(err) return err
console.log(data)
fs.readFile('./b.txt','utf8',(err,data)=>{
if(err) return err
console.log(data)
fs.readFile('./c.txt','utf8',(err,data)=>{
if(err) return err
console.log(data)
fs.readFile('./d.txt','utf8',(err,data)=>{
if(err) return err
console.log(data)
// ...回调地狱
})
})
})
})
为了解决类似这种异步操作带来的问题,使代码编写更加优雅且更具可读性,Promise 作为前端异步编程的解决方案诞生了;
上边的代码使用 Promise 进行包装处理,如下:
const fs = require('fs');
// 使用 Promise 封装处理 fs.readFile
function readFile(filePath, encoding) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, encoding, (err, data) => {
if (err) return reject(err) //失败
resolve(data) // 成功
})
});
}
// 使用方式
readFile('./a.txt','utf8').then((data) => {
console.log(data)
return readFile('./b.txt','utf8')
}).then((data) => {
console.log(data)
return readFile('./c.txt','utf8')
}).then((data) => {
console.log(data)
return readFile('./d.txt','utf8')
}).then((data) => {
console.log(data)
})
这种将异步回调包装成为 Promise 的处理方式也叫 Promisify,常用于 nodejs、小程序场景,使异步代码更简洁;
- 备注:
使用类库时仔细查看官方说明,目前很多类库都支持返回 Promise 实例,应尽量避免在外部进行重复包装;部分类库既支持 callback 也支持 Promise 形式;
九,Promise 优缺点
-
优点
- 优化异步代码,解决“回调地狱”问题,增强代码的可读性、可维护性;
- Promise 对象提供统一的接口,使得控制异步操作更加容易;
- 通过 catch 方法对异常进行统一的捕获处理;
-
缺点
- 无法取消 Promise,一旦创建它就会立即执行,无法中途取消;
- 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部;
- 当处于 Pending 状态时,是无法得知目前进展到哪一个阶段(刚刚开始还是即将完成);
十,结尾
本篇,对 Promise 进行了简单介绍,主要涉及以下几个点:
- Promise 简介和基本使用;
- Promise 和 callback 对比;
- Promise 的重要性和作用;
- Promise 使用场景:Promisify 封装;
- Promise 的优缺点、兼容性;
备注:本篇的定位仅仅是简单的认识和了解 Promise,并未涉及深入的介绍和分析,比如:Promise 的状态、执行顺序、链式调用、api 使用、异常处理等;
下一篇,将结合示例对 Promise 进行相关功能介绍与特性分析;
维护日志
- 20211019
- 更新部分示例代码,更符合相关知识点;
- 添加 promise 兼容性部分;
- 更新结尾部分、文章摘要;
- 20211024
- 扩充部分内容;
- 20211025
- 重新梳理大纲,调整一二级标题、扩充相关内容;
- 修改文章标题、摘要;