同步,异步
同步和异步是在计算机程序中用于描述执行任务时的两种不同方式。
同步执行指的是任务按顺序一个接一个地执行,每个任务完成之后才能进行下一个任务
。这就意味着在执行一个任务时,程序会一直等待该任务完成之后再去执行下一个任务。
异步执行指的是任务并不是按照顺序一个接一个地执行的,而是可以同时执行多个任务,并且在等待某个任务执行完成时,程序仍然可以继续执行其他任务。
异步操作并不一定计算量大或要等很长时间。只要你不想为 等待某个异步操作而阻塞线程执行,那么任何时候都可以使用。
JavaScript 采用事件循环(Event Loop)的机制来实现异步操作。
事件循环是一个持续运行的过程,它会从消息队列中取出消息,并将其分配给相应的事件处理程序来处理。
在 JavaScript 中,一些操作(如定时器、网络请求等)会被放入消息队列中,当它们完成时,就会添加到消息队列的末尾。
复制代码
promise
premise是es6新增的引用类型,是 JS 中进行异步编程
的新解决方案。通过new操作符实例化
三种状态
- 待定(pending)
- 兑现(fulfilled / “解决” resolved)
- 拒绝(rejected)
promise 状态的改变
- pending 变为 fulfilled
- pending 变为 rejected
说明: 只有这 2 种
, 且一个 promise 对象只能改变一次
无论变为成功还是失败, 都会有一个结果数据 成功的结果数据一般称为 value, 失败的结果数据一般称为 reason,当状态转换为兑现/拒绝 时,promise的状态就不再改变。
Promise.resolve
调用Promise.resolve
,可以实例化一个兑现(fulfilled)的promise
let p1 = Promise.resolve(3)
// 多余的参数会省略
let p2 = Promise.resolve(4,5,6)
console.log(p1)// Promise<resolved>:3
console.log(p2)// Promise<resolved>:4
// 传入错误的对象也会转为解决的promise
let p3 = Promise.resolve(new Error('err'))// Promise<resolved>:Error
console.log(p3)
复制代码
Promise.reject
Promise.reject()会实例化一个拒绝的promise
并抛出一个异步错误(这个错误不能被try/catch捕获,只能通过拒绝处理程序捕获)
let p1 = Promise.reject(3)
let p2 = Promise.reject(4,5,6)
console.log(p1)
p1.catch(err => console.log('catch捕获:'+err))
console.log(p2)
p2.catch(err => console.log('catch捕获:'+err))
let p3 = Promise.reject(Promise.resolve(3))
console.log(p3)
p3.catch(err => console.log('catch捕获:'+err))
复制代码
Promise.prototype.then()
Promise.prototype.then()是为promise实例添加处理程序的主要方法。接受两个参数:onResolved
处理程序和onRejected
处理程序,分别在promise进入“兑现”和“拒绝”状态执行
let p1 = new Promise((resolve,reject) => {
// setTimeout 属于宏任务
setTimeout(resolve('成功'),1000)
console.log('进入Promise1函数体')
})
// promise.then属于微任务
p1.then((res) => { console.log('resolve:'+res) },(rej) => {console.log('reject:'+rej) })
let p2 = new Promise((resolve,reject) => {
// setTimeout 属于宏任务
setTimeout(reject('失败'),1000)
console.log('进入Promise2函数体')
})
// promise.then属于微任务
p2.then((res) => { console.log('resolve:'+res) },(rej) => {console.log('reject:'+rej) })
复制代码
Promise.prototype.catch()
Promise.prototype.catch()方法用于给promise添加拒绝处理程序。只接收一个参数:onRejected处理程序
。(相当于调用Promise.prototype.then(null,onRejected))
let p1 = new Promise((resolve,reject) => {
// setTimeout 属于宏任务
setTimeout(reject('捕获失败'),1000)
console.log('进入Promise1函数体')
})
p1.catch(err => console.log(err))
复制代码
Promise.prototype.finally()
Promise.prototype.finally()
方法用于给promise添加onFinally处理程序,这个处理程序在promise状态改变时触发。
let p1 = new Promise((resolve,reject) => {
// setTimeout 属于宏任务
setTimeout(resolve('成功'),1000)
console.log('进入Promise1函数体')
})
p1.finally(() => { console.log('执行了:finally') })
let p2 = new Promise((resolve,reject) => {
// setTimeout 属于宏任务
setTimeout(reject('失败'),1000)
console.log('进入Promise2函数体')
})
p2.finally(() => { console.log('执行了:finally') })
复制代码
Promise.all / Promise.race
Promise类提供两个将多个promise实例组合成一个实例的静态方法:Promise.all
/ Promise.race
,合成后的状态取决于内部promise的行为
- Promise.all()
Promise.all()方法它接收一个由Promise对象组成的数组作为参数,并返回一个新的Promise对象。创建的promise会在内部promise全部解决之后再解决
或在数组中任何一个Promise被拒绝(rejected)
时被拒绝,并传递该Promise的拒因
给新的Promise对象。这个方法接收一个可迭代对象,返回一个新promise
const promises = [
fetch('https://api.example.com/data1'),
fetch('https://api.example.com/data2'),
fetch('https://api.example.com/data3')
];
Promise.all(promises)
.then(results => { // 处理所有Promise对象的结果 console.log(results[0]); console.log(results[1]); console.log(results[2]); })
.catch(error => { // 处理Promise对象拒绝的情况 console.error(error); });
复制代码
- Promise.race()
Promise.race()方法返回一个包装后的promise
,是一组集合中最先解决或拒绝的promise的镜像(返回第一个被处理的结果)。
let p1 = new Promise((resolve,reject) =>{
setTimeout(reject('失败1'),1000)
})
let p2 = new Promise((resolve,reject) =>{
setTimeout(resolve('成功1'),1000)
})
let p3 = new Promise((resolve,reject) =>{
setTimeout(reject('失败2'),1000)
})
let p4 = new Promise((resolve,reject) =>{
setTimeout(resolve('成功1'),1000)
})
Promise.race([p1,p2,p3,p4])
.then(res => console.log(res))
.catch(err => console.log(err))
复制代码
async/await
async关键字用于声明异步函数
,可以用在函数声明,函数表达式,箭头函数和方法上
async function foo(){ }
let bar = async function(){ }
let baz = async foo() => { }
class Qux {
async qux(){ }
}
复制代码
使用async关键字可以让函数具有异步特征,但总体上其代码仍然是同步求值
。
async function foo(){
console.log(1)
}
foo()
console.log(2)
// 1 , 2
复制代码
当使用return 关键字 / throw 关键字
// 使用 return 关键字
async function foo(){
console.log(1)
return '成功'
}
foo().then(console.log).catch(console.log)
console.log(foo())
// 使用 throw 抛出错误
async function foo(){
console.log(1)
throw '失败'
}
foo().then(console.log).catch(console.log)
console.log(foo())
复制代码
await关键字可以暂停异步函数代码的执行
,直到异步函数返回结果后再继续执行下面的代码。await关键字必须在异步函数中(async)
使用
async function myFunc() {
// 同步执行
console.log('before await');
let res = await new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('成功') },3000) })
// 3秒后执行
console.log('after await');
console.log(res)
}
myFunc()
复制代码
// promise 返回失败结果 需要配合try catch使用
async function myFunc() {
try{
console.log('before await');
let res = await new Promise((resolve,reject)=>{ setTimeout(()=>{ reject('失败') },3000) })
console.log('after await');
console.log(res)
}catch(err){
console.log(err)
}
}
myFunc()
复制代码
async/await 对于 promise的优势
- 代码更简洁易懂
- 错误处理更加直观(使用try/catch)
- 可读性更好
虽然 async/await
在代码书写上更加简洁易懂,但它本质上还是基于 Promise
实现的,因此在性能上与 Promise
并不存在太大差异。 它也需要在现代浏览器或 Node.js 环境中使用,对于老旧浏览器还是需要使用 Promise
等技术实现异步编程。