promise
1,Promise是ES6中的新的异步语法,解决了回调嵌套的问题:
new Promise((resolve)=>{
setTimeout(()=>{
resolve(1)
},1000)
}).then(val =>{
console.log(val); //1s后打印1,由resolve传过来
return new Promise((resolve)=>{ //里面可以嵌式promise
setTimeout(()=>{
resolve(2)
},1000)
})
// return 2 //也可以直接return一个值出去,不用等待
}).then(val => {
console.log(val); //按顺序打印1,2
})
2,状态切换
- promise实例有三个状态,
pending
,fulfilled
,rejected
// 初始状态 // 成功状态 // 失败状态 - promise实例在构造是可以传入执行函数,执行函数有两个形参
resolve
,reject
可以改变promise的状态,promise的状态一旦改变后不可再进行改变。 - 执行函数会在创建promise实例时,同步执行
3,then
异步执行
promise实例可以调用then
方法并且传入回调:
如果调用then
时,Promise实例是fulfilled
状态,则马上异步执行传入的回调。
如果调用then
时,Promise实例是pending
状态,传入的回调会等到resolve后再异步执行
例子:
let p = new Promise((resolve, reject)=>{
console.log(1);
resolve(2)
console.log(3);
})
p.then((val)=>{ //`fulfilled`状态,因为异步会慢一点
console.log(val);
})
//1 3 2
let p = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve(1)
},2000)
})
p.then((val)=>{
console.log(val); //`pending`状态,2s后执行
})
思路:需要用回调先保存到队列中,在resolve
后异步执行队列里的回调,在then
时判断实例的状态再决定是将回调推入队列,还是直接异步执行回调:
4,resolve
Promise实例的情况
resolve
的值有可能也是个promise实例,这时候就要用前述实例自己resolve
的值
let p = new Promise((resolve,reject) =>{ //promise1
resolve(new Promise((resolve2,reject2)=>{ //promise2
setTimeout(()=>{
resolve2(1)
},1000)
}))
})
p.then((val)=>{
console.log(val);
})
因此需要在promise1的resolve
函数中进行判断,是promise实例则在这个promise实例(promise2)后接一个then
,并且将promise1的resolve
作为回调传入promise2的then
5,实现链式调用
then
可以链式调用,而且前一个then
的回调的返回值,如果不是promise实例,则下一个then
回调的传参值就是上一个then
回调的返回值,如果是promise实例,则下一个then
回调的传参值,是上一个then
回调返回的promise实例的解决值(value)
let p = new Promise((resolve,reject) =>{
setTimeout(()=>{
resolve(1)
},1000)
})
p.then(val => { //这里.then返回的是一个新的promise实例
console.log(val);
return new Promise((resolve) => {
setTimeout(()=>{
resolve(2)
},1000)
})
}).then(val => {
console.log(val);
return 3
}).then(val => {
console.log(val); //依次打印1,2,3
})
既然能够链式调用,那么then
方法本身的返回值必定是一个Promise实例。那么返回的promise实例是不是自身呢?答案显而易见:不是。如果一个promise的then方法的返回值是promise自身,在new一个Promise时,调用了resolve方法,因为promise的状态一旦更改便不能再次更改,那么下面的所有then便只能执行成功的回调,无法进行错误处理,这显然并不符合promise的规范和设计promise的初衷。
因此 then
方法会返回一个新的promise实例
6,其他方法
- catch
- resolve
- reject
- all
- race
方法演示:
/*catch方法*/
let p = new Promise((resolve, reject) => {
reject(1)
})
p.catch(reason => { //一个语法糖,捕获错误,对应then
console.log(reason);
})
/*Promise.resolve*/
let p = Promise.resolve(1) //返回一个新的已解决的promise实例
/*Promise.reject*/
let p = Promise.reject(1) //返回一个新的已拒绝的promise实例
/*Promise.all*/
let p = Promise.all([
new Promise(resolve => {
setTimeout(() => {
resolve(1)
}, 1000)
}),
new Promise(resolve => {
setTimeout(() => {
resolve(2)
}, 2000)
}),
new Promise(resolve => {
setTimeout(() => {
resolve(3)
}, 3000)
}),
])
p.then(val => { //会等到所有promise都解决后才执行.then,等待3s后返回一个数组[1,2,3]
console.log(val);
})
/*Promise.race*/
let p = Promise.race([
new Promise(resolve => {
setTimeout(() => {
resolve(1)
}, 1000)
}),
new Promise(resolve => {
setTimeout(() => {
resolve(2)
}, 2000)
}),
new Promise(resolve => {
setTimeout(() => {
resolve(3)
}, 3000)
}),
])
p.then(val => { //以最早完成为准,等待1s后返回解决的1
console.log(val);
})
7,macrotask和mirotask
所谓macroTask
(宏任务)是指将任务排到下一个事件循环,microTask
(微任务)是指将任务排到当前事件循环的队尾,执行时机会被宏任务更早。Promise的标准里没有规定Promise里的异步该使用哪种,但在node和浏览器的实现里都是使用的miroTask
(微任务)
setTimeout(() => { //属于宏任务
console.log(1);
}, 0)
let p = Promise.resolve(2) //如果这里是Promise2,则打印顺序是1,2
p.then((val) => { //这里立即解决了属于微任务 打印顺序是2,1
console.log(val);
})
宏任务api包括:setTimeout
,setInterval
,setImmediate(Node)
,requestAnimationFrame(浏览器)
,各种IO操作,网络请求
微任务api包括:process.nextTick(Node)
,MutationObserver(浏览器)
MutaionObserver
演示:
let observer = new MutationObserver(()=>{
console.log(1);
})
let node = document.createElement('div')
observer.observe(node, { // 监听节点
childList: true // 一旦改变则触发回调函数 nextTickHandler
})
node.innerHTML = 1