1. Promise基本概念
- ES6中新增一个内置类:Promise,可以有效处理异步编程
1.1 从一个需求讲起
- AJAX串行需求:首先从服务器端基于/api/1获取数据,把数据作为参数基于/api/2获取其它数据,最后基于2的数据,基于/api/3获取数据
$.ajax({
url: '/api/1',
method: 'GET',
dataType: 'json',
success: function(result) {
console.log(result)
$.ajax({
url: '/api/2',
method: 'GET',
dataType: 'json',
success: function(result) {
console.log(result)
}
})
}
})
- Promise管理异步编程
const api1 = () => {
return new Promise(resolve => {
$.ajax({
url: '/api/1',
method: 'GET',
dataType: 'json',
success(result) {
resolve(result)
}
})
})
}
api1().then(result => {
console.log('第一次请求成功', result)
return api2();
}).then(result => {
console.log('第二次请求成功', result)
return api3();
}).then(result => {
console.log('第三次请求成功', result)
});
- 更简便的基于Promise的写法
(async function() {
let result = await api1();
console.log('第一次请求成功', result)
result = await api2();
console.log('第二次请求成功', result)
result = await api3();
console.log('第三次请求成功', result)
})();
1.2 js中的异步编程
- ajax请求
- 事件绑定
- 定时器
- Promise/async/await
- requestAnimationFrame
setTimeout(() => {
console.log(1);
}, 1000);
console.log(2);
// 先输出 2,后输出 1
2. Promise基本用法
2.1 必传executor
- new时,必须传递一个
executor
函数进来
let p1 = new Promise();
// Promise resolver undefined is not a function
2.2 new Promise的时候
2.2.1 会立即执行传递的executor函数
- 在executor函数中一般用来管控一个异步操作
- 不写异步操作,不改状态(不调用reslove,reject),的其他同步代码,会同步执行
- 执行resolve/reject时,会立即更改状态信息,但是不会立即通知方法执行(有异步效果)
- reslove改变状态是同步的,then执行回调是异步的
- executor函数有两个参数,resolve和reject,都是函数
2.2.2 new Promise类的一个实例p1,console.log(p1)
- 每一个promise实例都有[[PromiseState]]和[[PromiseResult]]
[[PromiseState]]
promise的状态:pending准备/fulfilled/resolved已兑现/rejected已拒绝[[PromiseResult]]
promise的值:默认undefined,一般存储成功的结果或者失败的原因- p1.__proto__指向promise.prorotype (
then/catch/finally
三个方法)
2.2.3 状态变更规则
- 执行resolve,控制实例的状态变为成功,传递的值是结果[[PromiseResult]]
- reject,控制实例的状态变为失败,传递的值是结果[[PromiseResult]]
- 一旦状态从pending发生改变,后边操作都无效(eg: 先resolve后reject,后操作无效)
- 代码中有报错,会触发rejected状态,值[[PromiseResult]]是报错原因
- 异步代码操作成功,此时通过执行resolve(),状态pending会变成resolved
2.2.4 代码执行顺序1
-
- new Promise的时候创建一个promise实例
-
- 执行executor,设置一个异步定时器
-
- 执行p1.then注入的两个方法,注入的方法会被保存起来(同步)
-
- 等待1000ms
-
- 执行定时器的回调函数,执行resolve改变promise的状态和值(resolve是异步)
-
- 基于之前then注入的两个方法,结合状态,执行一个
let p1 = new Promise(function(resolve, reject) {
setTimeout(() =>
reslove('ok')
}, 1000)
})
p1.then(result=> {
// p1状态为fulfilled时候,此函数执行,result->[[PromiseResult]]
}, reason=> {
// p1状态为rejected时候,此函数执行,reason->[[PromiseResult]]
})
2.2.5 代码执行顺序2
-
- 输出1
-
- reslove修改状态和值,通知then, 但是此时.then还未执行注入,不知道通知谁执行,把通知操作先保存,内部是放到等待任务队列中,这个操作本身是异步的,
new promise是同步的
,reslove通知注入执行是异步的
,此时p1已经是成功了,暂时存储
- reslove修改状态和值,通知then, 但是此时.then还未执行注入,不知道通知谁执行,把通知操作先保存,内部是放到等待任务队列中,这个操作本身是异步的,
-
- 输出2
-
- 开始注入方法,保存
-
- 输出3
-
- 异步队列,执行注入.then执行
let p1 = new Promise(function(resolve, reject) {
console.log(1)
resolve('OK')
console.log(2)
})
p1.then(result=> {
console.log('成功->', result)
}, reason=> {
console.log('失败->', reason)
})
console.log(3)
2.2.6 代码执行顺序3
-
resolve是异步的,不会立即处理,会放到等待队列
-
1s后修改状态和值,存入异步队列,此时方法已经注入好了
-
同步先输出1
-
执行then,输出2
let p1 = new Promise(resolve => {
setTimeout(()=> {
resolve('OK')
console.log(1)
}, 1000)
})
p1.then(result=> {
console.log(2)
})
3. Promise更多用法
let p1 = new Promise(resolve => {
resolve('OK')
})
// 上面可以简写为下面
// 创建一个状态为成功的promise实例
// let p1 = Promise.resolve('OK')
let p2 = p1.then(result => {
console.log('成功->', result)
}, reason => {
console.log('失败->', reason)
})
// p2是一个全新的promise实例
p2.then(result => {
console.log('成功->', result)
}, reason => {
console.log('失败->', reason)
})
- 执行.then返回一个全新的promise实例
3.1 promise实例状态和值的分析
3.1.1 new Promise出来的实例(p1)
- resolve/reject的执行控制其状态以及结果
- executor函数执行失败,导致状态失败,结果是报错原因
3.1.2 执行.then返回的新的promise实例(p2)
- 上一个p1.then注入的方法,无论哪个方法执行,只要执行不报错,新实例的状态就是fulfilled,执行报错,新实例的状态就是rejected,并且新实例p2的promise值,是方法返回的值
- 如果方法执行,返回的是一个新的promise实例,则此实例最后的状态,决定了.then返回的成功和失败,结果也是一样的
let p1 = new Promise(resolve => {
resolve('OK')
})
let p2 = p1.then(result => {
console.log('成功->', result)
return 10
}, reason => {
console.log('失败->', reason)
})
p2.then(result => {
console.log('成功->', result)
}, reason => {
console.log('失败->', reason)
})
// p1成功,输出 成功,ok
// p2是p1返回的成功,输出 成功,10
let p1 = new Promise((resolve, reject) => {
reject('No')
})
let p2 = p1.then(result => {
console.log('成功->', result)
return 10
}, reason => {
console.log('失败->', reason)
return 20
})
p2.then(result => {
console.log('成功->', result)
}, reason => {
console.log('失败->', reason)
})
// p1失败,输出 失败,No
// p2是p1返回的成功,输出 成功,20
let p1 = Promise.resolve('OK')
let p2 = p1.then(result => {
console.log('成功->', result)
return Promise.reject('No')
}, reason => {
console.log('失败->', reason)
return 20
})
p2.then(result => {
console.log('成功->', result)
}, reason => {
console.log('失败->', reason)
})
// p1成功,输出 成功,ok
// p2,输出 失败,No
3.1.3 顺延
- 对于失败的promise实例,如果没有编写的方法处理其结果,则会抛出异常,但不会阻碍其他代码执行
- 如果失败,.then最后接上一个的失败状态
- 在.then注入的方法的时候,如果其中某个方法没有传递,则会顺延到下一个then中具备相同状态需要执行的函数上
Promise.reject('No').then(result => {
console.log('成功', result)
return 10;
}).then(null, reason => {
console.log('失败', reason)
})
Promise.resolve('ok').then(null, reason=> {
console.log('失败', reason)
}).then(result => {
console.log('成功', result)
})
- 捕捉最后的错误用
catch
代替
Promise.resolve('ok').then(null, reason=> {
console.log('失败', reason)
}).then(result => {
console.log('成功', result)
}).then(result => {
console.log('成功', result)
}).catch(reason => {
console.log('失败', reason)
})
3.2 Promise.all & Promise.race
- Promise.all 等待所有promise实例都成功,整体返回的状态才成功,一个失败,整体失败
- Promise.race 看多个实例谁先处理完,先处理完的状态,无论失败成功,就是最后整体状态
const api1 = () => {}
const api2 = () => {}
const api3 = () => {}
const fn = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve(100)
}, 1000)
})
}
const AA = Promise.reaolve('AA')
let p = Promise.all([api1(), api2(), api3(), AA, fn(), 10]);
p.then(results = > {
// 每个结果拼成的数组,按照之前设定的顺序依次存储结果,不管谁先返回
console.log('所有结果', results)
}).catch(reason => {
// 只要一个失败,整体失败,走这里,立即结束处理,谁失败,记录失败的原因
console.log('err->', reason)
})