前言 

笔者之前写过相关的内容,但是讲的不够深入,这篇是更加综合的一个内容,结合网上的一些文章和例子,部分内容和例子引用:https://juejin.cn/post/7321996765977198611

promise

Promise是ES6异步编程的一种解决方案(目前最先进的解决方案是async和await的搭配(ES8),但是它们是基于promise的),从语法上讲,Promise是一个对象或者说是构造函数,用来封装异步操作并可以获取其成功或失败的结果。

为什么使用promise

1.可以避免多层异步调用嵌套问题(回调地狱)
2.Promise 对象提供了简洁的API,使得控制异步操作更加容易(js执行机制导致的异步问题)

promise的三种状态

1.pending: 等待中,或者进行中,表示还没有得到结果
2.resolved(Fulfilled): 已经完成,表示得到了我们想要的结果,可以继续往下执行
3.rejected: 也表示得到结果,但是由于结果并非我们所愿,因此拒绝执行

这三种状态不受外界影响,而且状态只能从pending改变为resolved或者rejected,并且不可逆

let p = new Promise((resolve, reject) => {
//状态改变是不可逆的,因此rejected改变后,后面的resolve就没用了
    reject('error')
    resolve("ok")
  }).then(res => {
    console.log(res,"1")
    return ("ok2")
  }).then(res => {
    console.log(res,"2")
  }).catch(err => {
  // 直接穿透前面的then函数,执行catch函数
    console.log(err,"3") //error
  })

//输出 :error 3

promise的静态方法

resolve( )

Promise.resolve(value)

  • value:成功的数据或 promise 对象
  • resolve()返回一个成功或失败的 promise 对象(不改变原promise对象状态)
//如果传入的参数为 非 Promise 类型的对象,返回结果为成功 promise 对象
Promise.resolve("成功").then(
  (value) => {
    console.log(value); // "成功"
  },
  (reason) => {
    console.log(reason)// 不会被调用
  },
);

//如果传入的参数为 Promise 对象,则参数的结果决定了 resolve 的结果
Promise.resolve(new Promise((resolve, reject) => {
  reject('error')
})).then(
  (value) => {
    console.log(value); // 不会被调用
  },
  (reason) => {
    console.log(reason) //error,注意这里的reason是then的第二个参数,因此执行的是catch的作用
  }
)

  

注意:多个resolve声明时,只有第一个声明成立

实现链式的调用:

前面任何操作出了异常,也都会传到最后失败的回调中处理

let p = new Promise((resolve, reject) => {
    resolve("ok")
  }).then(res => {
    console.log(res,"1")
    return ("ok2")
  }).then(res => {
    console.log(res,"2")
  }).catch(err => {
  // 直接穿透前面的then函数,执行catch函数
    console.log(err,"3") //error
  })

//输出 :ok  1
//      ok2  2

reject( )

Promise.reject(reason)

  • reason:失败的原因
  • 返回一个失败的 promise 对象(快速得到一个 promise 对象,且永远是一个失败的 promise 对象)
//如果传入的参数为 非 Promise 类型的对象,返回结果为失败的 promise 对象
Promise.reject("失败").then(
  (value) => {
    console.log(value); // 不会被调用
  },
  (reason) => {
    console.log(reason) // "失败"
  },
);

当内部是promise对象

const p = Promise.reject(new Promise((resolve, reject) => {
    resolve('ok')
}).then(res=>{
    console.log(res,"3")
    //3对应的是内部new出来的promise对象
})
)
//1对于的是p,他本身是一个promise,内部还包裹了一个promise
//catch返回的是promise.reject()内部的promise对象(此时他作为reject传递的对象),因此应该是fulfilled状态
//因为内部的promise对象返回的方法是resolve()
p.catch((res)=>{
    console.log(res,"1")
})
//2就是输出p
//但是由于Promise.reject()直接把状态变成rejected状态,因此p永远是rejected
console.log(p,"2")

all( )

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

语法:

const p = Promise.all([p1, p2, p3])

接受一个数组(迭代对象)作为参数,数组成员都应为Promise实例

如果参数包含非 Promise 对象(即普通的值或非 Promise 对象),它们会被视为已经解决的 Promise,且会立即被视为成功

实例p的状态由p1、p2、p3决定,分为两种:

  • 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数
  • 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数

注意,如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法

例子1:正常返回

const p1 = Promise.resolve(3);
const p2 = 1337;
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("foo");
  }, 100);
});

Promise.all([p1, p2, p3]).then((values) => {
  console.log(values); // [3, 1337, "foo"]
});

例子2(p2没有自己的catch方法,就会调用Promise.all()的catch方法)

const p1 = new Promise((resolve, reject) => {
  resolve('hello')
}).then((result) => result)

const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了')
}).then((result) => result)

Promise.all([p1, p2])
  .then((result) => console.log(result))
  .catch((e) => console.log(e)) // Error: 报错了

例子3(返回值组成一个数组,传递给p的回调函数,参数promise对象自定义了catch方法)

//resolved状态
const p1 = new Promise((resolve, reject) => {
    resolve('hello')
  })
    .then((result) => result)
    .catch((e) => e)
//rejected状态  
const p2 = new Promise((resolve, reject) => {
    throw new Error('报错了')
})
    .then((result) => result)
    .catch((e) => e)
//rejected状态
const p3 = new Promise((resolve, reject) => {
    reject('me too')
})
    .then((result) => result)
    .catch((e) => e)
//由于p2,p3自身定义了catch方法
//因此all直接用他们自己的catch方法,并且从.then中输出
  const p = Promise.all([p1, p2, p3])
    .then((result) => console.log(result)) 
// ["hello", Error: 报错了 at file xxx,"me too"]
    .catch((e) => console.log(e)) //不执行
  

race( )

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

语法:

const p = Promise.race([p1, p2, p3])

只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变率先改变的 Promise 实例的返回值则传递给p的回调函数

例子1

settimeout的格式

setTimeout(function, delay, arg1, arg2, ...);

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});

const p = Promise.race([promise1, promise2])
setTimeout(() => {
  console.log(p); //Promise {<fulfilled>: 'two'}
}, 1000);

allSettled( )

promise.allSettled()方法将一个 Promise 可迭代对象作为输入,并返回一个新的 Promise 实例

当所有输入的 Promise 都已敲定时(包括传入空的可迭代对象时,返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象数组

无论是fulfilled还是rejected状态都是传入对象数组中,所以都是直接使用.then即可

Promise.allSettled(iterable) //promise组成的可迭代对象,如数组
let p1=Promise.resolve(33)
let p2= new Promise((resolve) => setTimeout(() => resolve(66), 0))
let p3=99
let p4= Promise.reject(new Error("一个错误"))

let p=Promise.allSettled([p1,p2,p3,p4]).then(res=>{
    console.log(res,"对象数组")
    console.log(res[0].value,"索引为0的对象value")
    console.log(res[0].status,"索引为0的对象类型")
    console.log(res[3].reason,"索引为3的对象的报错")
})
console.log(p,"p")

throw抛出错误

  • 正常情况下,在通过 throw() 关键字抛出错误时,JavaScript 运行时的错误处理机制会停止执行抛出错误之后的任何指令
throw Error('foo'); 
console.log('bar'); // 这一行不会执行
// Uncaught Error: foo
  • 在 Promise 中抛出错误时,因为错误实际上是从消息队列中异步抛出的,所以并不会阻止运行时继续执行同步指令
Promise.reject(Error('foo')); 
console.log('bar'); 
// bar 
// Uncaught (in promise) Error: foo
//由此可知:异步错误只能通过异步的 onRejected 处理程序捕获

需要注意的是,try/catch仅能捕获同步代码中的错误,并不能捕获异步代码中的错误。对于异步代码,你可以使用.catch方法或async/await来捕获错误。

then()和 catch() 的 onRejected 处理程序在语义上相当于 try/catch。出发点都是捕获错误之后将其隔离,同时不影响正常逻辑执行

console.log('begin同步'); 
try { 
 throw Error('foo',"1"); 
 console.log("hhh")//此行不会执行,因为上面throw了一个错误
} catch(e1) { 
 console.log('caught error', e1); 
} 
console.log('continuet同步'); 

new Promise((resolve, reject) => { 
 console.log('begin 异步'); 
 reject(Error('bar',"2")); 
}).catch((e) => { 
 console.log('caught error', e); 
}).then(() => { 
 console.log('continue 异步'); 
}); 

promise的实例方法

①then()得到异步任务的正确结果

②catch()获取异常信息

注意:then方法可以接受两个参数,第一个参数为promise状态为成功的回调函数,第二个参数为promise状态为失败的回调函数(可以不写,一般用catch方法捕获promise状态为失败的异常信息)

let p = new Promise((resolve, reject) => {
   reject('OK1')
})
p.then(null,res => {
    console.log(res) //微任务先执行,后文有详细介绍
})
//此时的.then执行功能是.catch的功
//null顶替了第一个参数,第二个参数代表的就是rejected

function fn(flag) {
    return new Promise(function(resolve, reject) {
        if (flag === true) {
            resolve('promise状态为成功!')
        };
        if (flag === false) {
            reject('promise状态失败!')
        };
    }).then(res=>{
      console.log(res)
    }).catch(err=>{
      console.log(err)
    })
}
fn(true)
fn(false)
//多个resolve声明时,只有第一个声明成立

常见问题

1.一个 promise 指定多个成功/失败回调函数,都会调用吗?

当promise对象状态改变时,对应的成功/失败均会调用

//当promise改变为对象状态时都会调用
let p = new Promise((resolve, reject) => {
    resolve('OK')
  	resolve("22")
})
p.then(res => {
    console.log(res)
})
p.then(res => {
    console.log(res)
})
//ok
//ok

反之,即使声明了多个resolve,也只会传递第一个resolve声明

2.改变 promise 状态 和执行回调函数谁先谁后?
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("宏任务执行")
//由于resolve声明在宏任务中
//因此.then回调本质也是等宏任务执行完了才有参数输出
        resolve('ok')
    },1000) //宏任务
})
p.then(res => {
    console.log(res) //微任务
})
 Promise.resolve("hhh").then(res=>{
    console.log(res,"hhh")//微任务
})

3.promise 异常穿透

(1)当使用 promise 的 then 链式调用时,可以在最后指定失败的回调

(2)前面任何操作出了异常,都会传到最后失败的回调中处理

let p = new Promise((resolve, reject) => {
    resolve(Error("cuo"),)
  }).then(res => {
    console.log(res)
  }).catch(err => {
  // 直接穿透前面的then函数,执行catch函数
    console.log(err) 
  })
  

4.如何中断 promise 链?
let p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok')
  }, 1000)
}).then(res => {
  console.log(res) //想在此处中断
  return new Promise(() => { }) 
//返回 pendding 状态的 promise 的对象,中断!
}).then(res => {
  console.log(res)
}).then(res => {
  console.log(res)
})
  • 直接通过throw抛出错误对象
let p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok')
  }, 1000)
}).then(res => {
  console.log(res) //想在此处中断
  throw new Error('中断')
}).then(res => {
  console.log('ok1')
}).then(res => {
  console.log('ok2')
}).catch(error => {
  console.log('catch error:', error)
})
  • 使用reject
let p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok')
  }, 1000)
}).then(res => {
  console.log(res) //想在此处中断
//new一个新的promise之后,把他状态变为rejected,并且return出去
  return Promise.reject('error here')
}).then(res => {
  console.log('ok1')
}).then(res => {
  console.log('ok2')
}).catch(error => {
  console.log('catch error:', error)
})
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐