Promise如何使用

存在问题

1. 嵌套问题

在没有Promise之前,我们通常都是通过回调函数来表示程序的异步,回调函数是我们程序的延续,回调函数是javascript中实现异步的最简单的方式,等待同步代码执行完的时候,在某个时间段内再执行回调函数。如果回调需要上一个回调的结果,那就会形成嵌套, 比如:

request(url1,{},function(val1){
    request(url2, {data: val1}, function(val2){
        request(url3, {data: val2}, function(val3){
        
        }
    }
})
复制代码

嵌套多了就形成了我们常常说的回调地域。回调地域有以下问题点:

  • 难以追踪执行顺序,维护起来极其困难
  • 代码脆弱性,可能动一处,处处动
  • 程序异常执行异常,结果难以预测

2. 信任问题

信任问题产生的主要原因是把回调函数的控制权教给了第三方,这叫控制反转,我们无法预知第三方库到底什么时候会执行回调函数,所以我们会在回调函数里面做各种判断,比如像过早回调,过晚回调等等,而在每个这样的回调函数里面我们每次都要做这样重复的工作。大体情况如下:

  • 调用回调过早
  • 调用回调过晚
  • 多次调用回调
  • 不知是否回调

所以Promise就是来解决上述问题的

如何解决上述存在的问题

1. 嵌套问题

Promise在原型上定义了一个then 方法,并且会返回一个新的Promise, 这样就形成了链式调用,这样也决定了调用顺序的问题,比如下面:

function ajax(url, data){
    return new Promise((resolve, reject)=>{
        request(url, data, function(res){
            resolve(res)
        })
    })
}

ajax()
 .then((res1)=> ajax(url1, res1))
 .then((res2)=> ajax(url2, res2))
 .then((res3)=> ajax(url3, res3))

复制代码

代码可读性提升了,顺序也可以很好的追踪到

2. 信任问题

  1. 过早调用。

Promise的then方法的回调函数总是异步的,存在于微任务队列,所以不存在过早

  1. 过晚调用

Promise在状态被敲定后,then方法注册的回调函数一定在下一次异步执行前调用,所以不存在过晚

  1. 多次调用

Promise在状态被敲定后,是不能被改变的,所以只能执行一次回调

  1. 不知是否调用

只要注册了then的回调函数就一定会被调用

Promise 规范

Promise使用了 Promise/A+ 规范,标准参考 英文版 中文版

Promise原型方法

1. then

then方法返回一个新的Promise, 最多接受两个参数,一个是成功回调,另一个是失败回调

// p.then(onFulfilled[, onRejected]);

const promise1 = new Promise((resolve, reject) => {
  // resolve('success');
  reject('error!');
});

promise1.then((value) => {
  console.log(value);
},(reason)=>{
  console.log(reason)
});
复制代码

then的回调可以return返回值,规则如下:

  • 如果返回的是一个值,那么then返回的Promise就成为接受状态,并将这个值作为接受状态的回调函数的参数
const promise1 = new Promise((resolve, reject) => {
  resolve('success');
});

promise1.then((value) => {
  console.log(value);
  return 1
}).then((res)=>{
  console.log(res) // 1
})
复制代码
  • 如果没有返回值,那么then 返回的Promise就成为接受状态,并且接受状态的回调函数的参数是undefined
const promise1 = new Promise((resolve, reject) => {
  resolve('success');
});

promise1.then((value) => {
  console.log(value);
}).then((res)=>{
  console.log(res) // undefined
})
复制代码
  • 如果抛出一个错误,那么then返回的Promise就成为拒绝状态,并且将该错误信息作为拒绝状态的回调函数的参数
const promise1 = new Promise((resolve, reject) => {
  resolve('success');
});

promise1.then((value) => {
  console.log(value);
  throw 'error'
}).then(null, (error)=>{
  console.log(error) 
})
复制代码
  • 如果是一个接受状态的Promise, then 返回的Promise就是接受状态,并且将那个Promise的接受状态的回调函数的参数值作为该Promise接受状态回调函数的参数值
const promise1 = new Promise((resolve, reject) => {
  resolve('success');
});

promise1.then((value) => {
  console.log(value);
  return Promise.resolve(22)
}).then((res)=>{
  console.log(res) // 22
})
复制代码
  • 如果是一个拒绝状态的Promise, then 返回的Promise就是拒绝状态,并且将那个Promise的拒绝状态的回调函数的参数值作为该Promise拒绝状态回调函数的参数值
const promise1 = new Promise((resolve, reject) => {
  resolve('success');
});

promise1.then((value) => {
  console.log(value);
  return Promise.reject('error')
}).then(null, (error)=>{
  console.log(error) // error
})
复制代码
  • 如果是一个待定状态的Promise, then 返回的Promise就是待定状态,该Promise的终态和那个Promise的终态相同,而且该Promise的终态下的回调函数的参数就是那个Promise终态下的回调函数的参数值
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(123)
  },2000)
})

Promise.resolve().then(() => {
  return p1
}).then((res) => {
  console.log(res) // 2s 后输出 123
})
复制代码

2. catch

catch也会返回一个Promise, 并且处理拒绝的情况, .catch() 其实只是没有给 onFulfilled 预留参数位置的 .then() 而已。可以改成成这样

Promise.prototype.catch = function(onRejected){ 
  return this.then(null, onRejected)
}
复制代码

使用catch 捕捉错误

var p1 = new Promise(function(resolve, reject) {
  resolve('Success');
});

p1.then(function(value) {
  console.log(value); // "Success!"
  throw 'oh, no!';
}).catch(function(e) {
  console.log(e); // "oh, no!"
  throw 'o'
})
复制代码

3. finally

finally() 方法返回一个Promise。在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。这为在Promise是否成功完成后都需要执行的代码提供了一种方式。
这避免了同样的语句需要在then()和catch()中各写一次的情况。

// 比如加载动画
var loading = true

var p1 = new Promise(function(resolve, reject) {
  resolve('Success');
});

p1.then(function(value) {
  
}).catch(function(e) {
  
}).finally(function(){
  loading = false
})
复制代码

Promise 静态方法

1. all

all方法接受一个iterable类型(注:Array,Map,Set都属于ES6的iterable类型)参数,只返回一个promise实例,执行resolve的条件是,iterable中的promise的resolve都结束,并将所有resolve回调返回的结果组成数组,作为then的接受状态的回调函数的参数值;执行reject的条件是,iterable中只要有一个reject,并将这个reject的错误信息作为then的拒绝状态回调的参数值。
resolve的情况

// 
var p1 = new Promise((resolve, reject) => {
  resolve(1)
})
var p2 = new Promise((resolve, reject) => {
  resolve(2)
})
var p3 = new Promise((resolve, reject) => {
  resolve(3)
})

Promise.all([p1, p2, p3]).then((res) => {
  console.log(res) //  [1, 2, 3]
})
复制代码

reject的情况

var p1 = new Promise((resolve, reject) => {
  resolve(1)
})
var p2 = new Promise((resolve, reject) => {
  reject('error 2')
})
var p3 = new Promise((resolve, reject) => {
  resolve(3)
})

Promise.all([p1, p2, p3]).then((res) => {
  console.log(res) 
}, (error) => {
  console.log(error) // error 2
})
复制代码

2. allSettled

allSettled()方法返回一个所有给定promise都已经resolve 或者 reject 的promise, 并且返回一个对象数组,对象就是promise的结果

var p1 = new Promise((resolve, reject) => {
  resolve(1)
})
var p2 = new Promise((resolve, reject) => {
  reject('error 2')
})

Promise.allSettled([p1, p2]).then((res) => {
  res.forEach(item => {
    console.log(item)
  })
})

//{status: "fulfilled", value: 1}
//{status: "rejected", reason: "error 2"}
复制代码

3. any

这个方法用于返回第一个成功的 promise 。只要有一个 promise 成功此方法就会终止,它不会等待其他的 promise 全部完成。接受一个iterable类型参数,没有一个promise成功就执行onRejected回调。
resolve的情况

var p1 = new Promise((resolve, reject) => {
  resolve(1)
})
var p2 = new Promise((resolve, reject) => {
  resolve(1)
})
var p3 = new Promise((resolve, reject) => {
  reject('error 3')
})

Promise.any([p1, p2, p3]).then((res) => {
  console.log(res) // 1
})
复制代码

reject的情况

var p1 = new Promise((resolve, reject) => {
  reject(1)
})
var p2 = new Promise((resolve, reject) => {
  reject('error 2')
})

Promise.any([p1, p2]).then((res) => {
  console.log(res)
},(error)=>{
  console.log(error) // AggregateError: All promises were rejected
})

复制代码

4. race

race方法返回一个promise, 一旦迭代器中的某一个promise接受或者拒绝,返回的promise也就接受或者拒绝,就是谁先敲定状态就返回谁的状态。方法接受一个iterable类型参数。

var p1 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 500, "one");
});
var p2 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 100, "two");
});

Promise.race([p1, p2]).then(function(value) {
  console.log(value); // "two"
  // 两个都完成,但 p2 更快
});
复制代码

5. resolve

resolve返回一个解析过的promise对象,就是一个接受状态的promise。

参数如下:

  • 一个值,该值作为then返回的promise接受状态的回调参数
  • 没传或者undefined, 将undefined作为then返回的promise接受状态的回调参数
  • promise对象,直接返回这个promise对象
var p1 = Promise.resolve(123)
var p2 = Promise.resolve()
var p3 = Promise.resolve(p1)

p1.then((res) => {
  console.log(res)
})
p2.then((res) => {
  console.log(res)
})
p3.then((res) => {
  console.log(res)
})
// 123
// undefined
// 123
复制代码

6. reject

reject返回一个拒绝状态的promise对象, Promise.reject(reason) 参数reason拒绝的原因

var p1 = Promise.reject(new Error('error'))

p1.then(null, (reason) => {
  console.log(reason)
})

//  Error: error
复制代码

promise的相关信息就先介绍到这,下篇我们来通过Promise/A+ 的规范来手写一个MyPromise,并且实现以上方法,敬请期待

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享