什么是 promise?
简单的说它是一个异步流程的控制手段. 是一个代表了异步操作最终完成或者失败的对象.
promise 的优点
promise 解决了回调地狱的问题
promise 可以支持多个并发的请求
promise 的错误传播机制可以统一的处理错误信息
回调地狱问题
在传统的 Ajax 调用过程中, 下面以 jQuery.Ajax 为例. 如果需求中要多次进行 Ajax 交互, 并且上一次的返回结果还要被下一次的 Ajax 使用, 代码基本上会变成:
- $.Ajax({
- type: "POST",
- url: "some.php",
- data: "name=John&location=Boston",
- success: function(msg){
- $.Ajax({
- type: "POST",
- url: "some.php",
- data: msg,
- success: function(msg2){
- ...// 多次调用
- }
- });
- }
- });
现在还只是两次调用关系, 如果是多次调用将会引发下面的问题
多次调用不利于代码的管理于维护
发生错误时不能及时准备的定位错误位置
只要又一次不成功就不能进行下面的逻辑, 不方便进行错误处理.
promise 的链式调用就很好的解决了这个问题
Promise 的三种状态
Pending Promise 对象实例创建时候的初始状态
resolve 可以理解为成功的状态
Reject 可以理解为失败的状态
promise 中的状态只能是从等待状态转换到成功状态或者失败状态, 且状态转变之后不可逆.
一个简单的 promise
- let p = new Promise((resolve, reject) => {
- console.log(1)
- });
- console.log(2)
- //1 2
promise 里面只接受一个参数, 叫做执行器函数, 这个函数会同步执行. 也就是说上面代码中的箭头函数被同步执行, 得到的结果也就是 1 和 2
promise 中的 then
每一个 promise 的实例上都有一个 then 方法, 这个方法上有两个参数, 一个是成功的回调, 一个是失败的回调. 而取决于成功或者失败的是 promise 的执行器函数中执行的是成功还是失败.
- let p = new Promise((resolve, reject) => {
- console.log(1);
- resolve();// 调用 resolve 会走到 then 的成功回调
- //reject();// 调用 resolve 会走到 then 的失败回调
- });
- p.then(
- () => {
- console.log('成功')
- }
- , () => {
- console.log('失败')
- })
- //1 成功
如果既不调用 resolve 也不调用 reject,promise 则一直处于等待状态, 也就不会走到 then 方法.
- let p = new Promise((resolve, reject) => {
- console.log(1);
- });
- p.then(
- () => {
- console.log('成功')
- }
- , () => {
- console.log('失败')
- })
- //1
如果你既调用 resolve 也调用 reject, 那么谁在前面执行就走谁的对应回调函数
- let p = new Promise((resolve, reject) => {
- console.log(1);
- resolve()// 先调用成功
- reject()
- });
- p.then(
- () => {
- console.log('成功')
- }
- , () => {
- console.log('失败')
- })
- //1 成功
- let p = new Promise((resolve, reject) => {
- console.log(1);
- reject()// 先调用失败
- resolve()
- });
- p.then(
- () => {
- console.log('成功')
- }
- , () => {
- console.log('失败')
- })
- //1 失败
如果代码出错则会直接走 reject 的回调
- let p = new Promise((resolve, reject) => {
- console.log(1);
- throw new Error('出错了~')
- });
- p.then(
- () => {
- console.log('成功')
- }
- , () => {
- console.log('失败')
- })
- //1 失败
一个 promise 的实例可以 then 多次
- let p = new Promise((resolve, reject) => {
- resolve('成功了');
- });
- p.then((data) => {
- console.log(data)// 成功了
- });
- p.then((data) => {
- console.log(data)// 成功了
- })
利用 promise 解决回调地狱
能够规避异步操作中回调地狱的问题, 其本质取决于 promise 的链式调用. 假设需求如下, a.txt 文件的内容为 b.txt,b.txt 文件的内容是一段描述文字, 现在要求用 a.txt 的得到最终的描述文字, 代码如下:
- let fs = require('fs');
- // 首先将异步方法封装在一个 promise 中, 异步结果成功调用 resolve 方法, 失败调用 reject 方法.
- function read(url) {
- return new Promise((resolve, reject) => {
- fs.readFile(url, 'utf8', function (err, data) {
- if (err) reject();
- resolve(data);
- })
- })
- }
- // 因为 read 方法返回的是一个 promise, 所以可以使用 promise 的 then 方法
- read('a.txt').then((data) => {
- // 第一次异步成功后拿到结果继续返回一个 promise 可以实现链式调用.
- console.log(data);//b.txt
- return read(data);
- }, (err) => { }).then((data) => {
- // 最后两次的结果分别对应两次异步的返回内容
- console.log(data)// 描述文字
- }, (err) => { })
总结: 如果一个 promise 的 then 方法中还返回另一个 promise, 那么这个 promise 的成功状态会走到外层 promise 的下一次 then 方法的成功, 如果失败, 返回外层 promise 下一次 then 的失败.
promise 的链式调用
如果 then 中返回的是一个普通值, 就会走到下一次 then 的成功回调.
- read().then((data) => {
- return 111
- }, (err) => {
- }).then((data) => {
- console.log(data)//111
- }, (err) => {
- })
如果 then 中返回的是一个错误, 就会走到下一次 then 的失败回调.
- read().then((data) => {
- throw new Error('出错了~')
- }, (err) => {
- }).then((data) => {
- console.log(data)
- }, (err) => {
- console.log(err)// 出错了~
- })
如果 then 中什么也不返回, 就会走到下一次 then 的成功回调, 得到的值为 undefined.
- read().then((data) => {
- cons.log(111)
- }, (err) => {
- }).then((data) => {
- console.log(data)//undefined
- }, (err) => {
- })
如果想统一处理错误内容, 可以使用 catch.
- read().then((data) => {
- throw new Error('出错了~')
- }, (err) => { }).then((data) => {}, (err) => {}).catch((err)=>{
- // 错误处理
- })
统一处理错误后, 还可以使用 then.
- read().then((data) => {
- throw new Error('出错了~')
- }, (err) => { }).then((data) => {}, (err) => {}).catch((err)=>{
- // 错误处理
- }).then((data) => {}, (err) => {})
Promise.all()
all 方法可以处理多个请求并发的问题. 参数是一个数组. all 方法调用后会返回一个新的 promise.
- let fs = require('fs');
- function read(url) {
- return new Promise((resolve, reject) => {
- fs.readFile(url, 'utf8', function (err, data) {
- if (err) reject();
- resolve(data);
- })
- })
- }
- Promise.all([read('1.txt'), read('2.txt')]).then((data) => {
- console.log(data)//[ '文本 1 内容', '文本 2 内容' ]
- }, (err) => {
- console.log(err);
- })
在 all 方法中一个失败了就全部失败, 所以都成功了才会走成功回调.
- Promise.all([read('1.txt'), read('3.txt')]).then((data) => {
- console.log(data)
- }, (err) => {
- console.log('失败了');// 失败了
- })
Promise.race()
多个请求中, 谁的返回数据最快, 结果就是谁
- Promise.race([read('1.txt'), read('2.txt')]).then((data) => {
- console.log(data)//// 文本 2 内容
- }, (err) => {
- console.log('失败了');// 失败了
- })
Promise.resolve()
返回一个成功的 Promise
- Promise.resolve('123').then((data) => {
- console.log(data)//123
- })
Promise.reject()
返回一个失败的 Promise
- Promise.resolve('123').then((data) => {
- console.log('err', data)//err 123
- })
如果你想手写一个 promise, 请戳下面的链接: 手写 promise
来源: https://juejin.im/post/5bed21156fb9a04a0c2e025d