前言
关于Promise的知识点,一定要认真多读几遍阮一峰老师的ES6电子书,照着案例demo自己手写几遍,熟悉每个知识点已经promise的概念,特性和原理。本文是阅读了阮一峰老师的文档然后自己总结下自己的理解。基本是搞懂了promise的概念,后面计划再写一篇手写promise源码和结合实际需求的应用场景再总结一篇业务中使用promise对象的使用方法。
文中有错误的地方,欢迎大佬指正。
概念
Promise 是异步编程的一种解决方案,可以将异步任务按同步方式处理。从理解上说Promise是一个容器,里面存放异步任务。在语法上 Promise是一个对象,有统一的API处理各种异步操作。有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
基本语法
Promise对象是一个构造函数,可以生成实例
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise 构造函数接受一个函数作为参数,该函数内还有两个参数,resolve和reject也是两个函数。
resolve函数的作用是将 状态从 pending 变为 fulfilled,即进行中到已成功,一般在异步任务执行成功后调用,并且会将执行的结果以参数形式传递出去 resolve(data)。
reject函数的作用是将状态从 pending 变为 rejected,即进行中到已失败,一般在异步任务执行失败后调用,也会将失败信息传递出去 reject(error)
Promise构造函数生成实例后,可以使用then()方法 来指定对应 resolve 和reject 的回调函数
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then((res)=>{
//succeess 对应resolve函数的回调
//res的值是从resolve函数的参数传递出来的
console.log(res)
}, (err)=>{
//failure 对应reject函数的回调
//err 的值是从reject函数的参数传递出来的
});
then() 方法 里面有两个回调函数作为参数,第一个回调函数就是 状态变为 resolved 时调用,第二个回调函数是状态变为 rejected是调用。
两个回调函数是可选的,同时两个回调函数的参数也都是接收 resolve 和reject 传递出来的参数。
const timeout = (ms) => {
return new Promise(function (resolve, reject) {
setTimeout(()=>{resolve('resolve')}, ms)
})
}
timeout(1000).then((res) => {
console.log(res)
})
//1秒后 resolve
特点
1)Promise对象状态不受外界影响
Promise 有三种状态 进行中(pending)已成功(fulfilled)已失败(rejected)
状态依据异步操作的结果来改变,其他操作无法改变状态。
//Promise的状态只受执行了 resolve或者reject 回调函数来改变状态,Promise内部其他的代码不会改变状态
new Promise(()=>{})
//Promise {<pending>}
//[[Prototype]]: Promise
//[[PromiseState]]: "pending"
//[[PromiseResult]]: undefined
new Promise((resolve)=>{
resolve()
})
//Promise {<fulfilled>: undefined}
//[[Prototype]]: Promise
//[[PromiseState]]: "fulfilled"
//[[PromiseResult]]: undefined
new Promise((resolve,reject)=>{
reject()
})
//Promise {<rejected>: undefined}
//[[Prototype]]: Promise
//[[PromiseState]]: "rejected"
//[[PromiseResult]]: undefined
//没有执行 resolve 或者 reject 回调函数 其他的同步,异步任务执行不会改变
new Promise(function (resolve, reject) {
console.log(234)
})
//234
//Promise {<pending>}
//[[Prototype]]: Promise
//[[PromiseState]]: "pending"
//[[PromiseResult]]: undefined
new Promise(function (resolve, reject) {
setTimeout(() => console.log(234), 3000)
})
//Promise {<pending>}
//[[Prototype]]: Promise
//[[PromiseState]]: "pending"
//[[PromiseResult]]: undefined
2)Promise状态一旦执行不会再改变
promise执行了回调函数以后 状态会进行以下改变
pending --> fulfilled 进行中变为成功
pendign -- > rejectd 进行中变为失败
状态一旦改变就稳定会一直保持这个结果不会再变,再添加回调函数也还是这个状态
//执行了resolve 回调函数 状态变化,后续再执行reject回调 也不会再改变状态
new Promise((resolve, reject) => {
resolve()
reject()
}).then((res) => {
console.log(res)
}).catch((err) => {
console.log(err)
})
//resolve
//[[Prototype]]: Promise
//[[PromiseState]]: "fulfilled"
//[[PromiseResult]]: undefined
3)比较注意的点
1.新建 Promise 实例会立即执行,无法中途取消
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
新建Promise实例 会立即执行,首先执行 内部的同步任务打印出 'Promise',后面then()方法是异步任务暂时挂起,接着执行后面的同步任务 打印出 'Hi' ,然后再执行异步任务 打印出 ' resolved'
可以使用函数封装后,调用函数再执行Promise操作
const pFun = ()=>{
return new Promise((resolve)=>{
console.log(123)
})
}
pFun()
// 123
2.如果不设置回调函数,Promise内部抛出的错误,不会反应到外部
//如果不执行回调函数,内部抛出的错误不会反应到外部
new Promise(function (resolve, reject) {
new Error('123')
})
//Promise {<pending>}
//[[Prototype]]: Promise
//[[PromiseState]]: "pending"
//[[PromiseResult]]: undefined
3.同样不执行回调函数,Promise内部异步任务执行完的结果不会传递到外面
//Promise内没有执行 resolve回调函数 ,所有then函数没有执行,结果没有传递出来
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
//执行resolve回调函数
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
//Promise
// Hi!
//resolved.
4)回调嵌套
如果p2 的resolve函数的参数是另一个p1,一个异步操作的结果返回了另一个异步操作。这时候p2的状态是由p1决定的,下面代码 p1始终是pending,p2虽然执行了resolve,但接收参数是p1,所以p2的状态也是pending而不是 resolved
const p1 = new Promise(()=>{})
const p2 = new Promise((resolve, reject)=>{
resolve(p1)
})
p2.then((res)=>{
console.log(res)
})
//[[Prototype]]: Promise
//[[PromiseState]]: "pending"
//[[PromiseResult]]: undefined
下面代码p1是3秒后reject ,p2是一秒后执行resolve,参数是p1 ,此时p1是等待状态,所以p2也是 pending,3秒后 p1 reject Error ,p2的 catch 会捕获异常错误。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('fail'))
}, 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve(p1), 1000)
})
p2.then(res => console.log(res)).catch(err => console.log(err))
//Promise {<pending>}
//[[Prototype]]: Promise
//[[PromiseState]]: "pending"
//[[PromiseResult]]: undefined
//Error: fail
5)调用resolve 或者reject 不会中断后面的任务执行,并且如果是同步任务还会先执行,then方法属于异步任务
new Promise((resolve, reject)=>{
resolve(123)
console.log(345)
}).then(res=>{console.log(res)})
//345
// 123
new Promise((resolve, reject) => {
reject(new Error('fail'))
console.log('error')
}).catch(err => console.log(err))
//error
// Error: fail
Promise.prototype 原型的方法
1)Promise.prototype.then()
Promise实例拥有then方法,then是定义 在Promise的原型对象上的,作用是给Promise实例添加 resolved和rejected 状态改变的回调函数,then方法有两个参数,都是回调函数。第一个是resolved的回调函数,第二个是rejected的回调函数。then()方法返回的是新的Promise实例,不是原来的Promise实例。因此then方法可以采用链式调用,then()调用后面再接另一个then(),可以处理异步操作嵌套。
const getList = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({infoId: '1234'})
}, 1000)
})
}
const getInfo = ({infoId}) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({infoId,datas: '...infoDatas'})
}, 1000)
})
}
// 因为getList和 getInfo里 是resolve,没有判断执行reject函数,使用try catch捕获外部异常。
try {
getList().then((params) => {
return getInfo(params)
}).then((result) => {
console.log('result:', result)
}).catch(err => console.log(err))
} catch (e) {
console.log(e)
}
//result: {infoId: '1234', datas: '...infoDatas'}
2)Promise.prototype.catch()
捕获 Promise 实例的 rejected状态的异常,也是 Promise构造函数 原型上的方法
catch方法 参数是一个回调函数 和then方法的第二个回调函数一样 ,回调函数的参数接收 rejected状态 传递出来的错误信息。
const p3 = new Promise((resolve, reject)=>{
reject(new Error('error'))
})
p3.catch(err=>console.log(err))
//Error: error
//catch 和then 的第二个回调函数一样 处理抛出来的错误异步操作, 如果都加上 执行了then方法就不再执行 catch 方法
const p3 = new Promise((resolve, reject) => {
reject(new Error('error'))
})
p3.then(null, (err) => console.log('then:', err))
.catch(err => console.log('catch:', err))
//then: Error: error
注意点: then() 方法指定的回调函数,如果运行中抛出错误,也会被 catch() 方法捕获。
const p = new Promise((resolve, reject)=>{
throw new Error('error info')
})
p.catch(err => console.log(err))
// Error: error info
如果执行了resolve 状态已经改变为fulfilled,后面再抛出异常catch也不会捕获到了。
const p = new Promise((resolve, reject) => {
resolve('is ok')
throw new Error('error info')
})
p.then(res => console.log(res))
.catch(err => console.log(err))
// is ok
Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
getList('/a.json').then(res => {
return getInfo(res.url)
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
总共三个promise对象,其中一个promise实例,两个then()方法,任何一个抛出错误,都会被最后一个catch()方法捕获到,而then()的第二参数只能捕获当前promise对象的异常错误, 所以尽量使用catch()方法来处理异常错误信息。
如果没有写catch(),promise内部会吃掉错误
跟传统的try/catch代码块不同的是,如果没有使用catch()方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。
const asyncThing = ()=>{
return new Promise((resolve, reject)=>{
resolve(x+1)
})
}
asyncThing().then(()=>{
console.log('is ok')
})
setTimeout(()=>{
console.log(123)
},1000)
// Uncaught (in promise) ReferenceError: x is not defined
//123
x没有声明,promise内部报错会打印出错误信息,但是后面的任务还是继续执行了,promise内部错误没有影响到外部的代码执行
3)Promise.prototype.finally()
不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。finally 回调函数不接收任何参数,也不依赖状态是否 fulfilled 或者 rejected
const getDatas = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('results')
}, 1000)
})
}
const queryDatasHandle = () => {
setLoading(true)
getDatas().then((res) => {
console.log(res)
}).catch(err => console.log(err))
.finally(() => {
setLoading(false)
})
}
queryDatasHandle()