ES6 入门第五章
Promise
Promise 是什么?
Promise 是 ES6 引入的异步编程的新解决方案(旧的是谁?旧的解决方案是纯回调函数)。语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
用来封装异步操作并可以获取其成功或失败的结果。
Promise的特点
(1)对象的状态不受外界影响。Promise
对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise
对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。
Promise 的状态改变
1)pending 变成 fulfilled
2)pending 变成 rejected
说明:只有这两种状态改变情况,且一个Promise对象只能改变一次状态
无论变成成功还是失败,都会有一个结果数据
成功的结果数据一般称为 value ,失败的结果数据一般称为 reason
Promise缺点
-
无法取消
Promise
,一旦新建它就会立即执行,无法中途取消。 -
如果不设置回调函数,
Promise
内部抛出的错误,不会反应到外部。 -
当处于
pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
Promise 的基本流程
Promise基本使用
步骤:
- 创建一个新的Promise对象
- 执行异步操作任务
- 1 如果成功了,调用resolve(value)
2. 如果失败了,调用reject(reason)
//1. 创建一个新的Promise对象
const p = new Promise((resolve,reject) => {//执行器函数
//2.执行异步操作任务
setTimeout(() => {
const time = Date.now() //如果当前时间是偶数就代表成功,否则代表失败
if(time % 2 == 0 ){ //3.1如果成功了,调用resolve(value)
resolve('成功的数据,time=' + time)
} else { //3.2如果失败了,调用reject(reason)
reject('失败的数据,time=' + time)
}
},1000)
})
p.then(
value => { //接收得到成功的value数据 onResolved
console.log('成功的回调',value)
},
reason => { //接收得到失败的reason数据 onRejected
console.log('失败的回调',reason)
}
)
为什么要用 Promise?
- 指定回调函数的方式更加灵活(灵活指的是时间的问题)
1) 旧的:必须在启动异步任务前指定
2)Promise:启动异步任务 => 返回 Promise 对象 => 给 Promise 对象绑定回调函数(甚至可以在异步任务结束后指定多个)
例子:创建一个音频文件
//成功的回调函数
function successCallback(result){
console.log("声音文件创建成功:" + result);
}
//失败的回调函数
function failureCallback(result){
console.log("声音文件创建失败:" + error);
}
1)使用纯回调函数
//1. 使用纯回调函数
creatAudioFileAsync(audioSettings, successCallback, failureCallback)
2)使用Promise
const promise = createAudioFileAsync(audioSettings);
setTimeout(()=> {
promise.then(successCallback, failureCallback);
},3000)
-
支持链式调用,可以解决回调地狱问题
-
什么是回调地狱?
回调函数嵌套调用(多个串联的异步操作:第一个执行完,再执行第二个……),外部回调函数异步执行的 结果是嵌套的回调执行的条件
//回调地狱 doSomething(function(result){ doSomethingElse(result,function(newResult){ doThirdThing(newResult,function(finalResult){ console.log('Got the final result:' + finalResult) },failureCallback) },failureCallback) },failureCallback)
-
回调地狱的缺点?
不便于阅读
不便于异常处理
-
解决方案
Promise链式调用
//使用Promise的链式调用解决回调地狱 doSomething() .then(function(result){ return doSomethingElse(result) }) .then(function(newResult){ return doThirdThing(newResult) }) .then(function(finalResult){ console.log('Got the final result:' + finalResult) }) .catch(failureCallback)
-
终极解决方案
async/await
// async/await:回调地狱的终极解决方案 async function request() { try{ const result = await doSomething() const newResult = await doSomethingElse(result) const finalResult = await doThirdThing(newResult) console.log('Got the final result:' + finalResult) } catch(error) { failureCallback(error) } }
如何使用 Promise
-
Promise构造函数:Promise(excutor){}
1)excutor 函数:执行器 (resolve,reject) => {}
2)resolve 函数:内部定义成功时,我们调用的函数 value => {}
3)reject函数:内部定义失败时,我们调用的函数 reason => {}
说明:excutor 会在 Promise 内部立即同步回调,异步操作在执行器中执行
原型方法
1)Promise.prototype.then 方法
2)Promise.prototype.catch 方法
3)Promise.prototype.finally 方法
自身的方法
all()、race()、reject()、resolve()、try()
-
Promise.prototype.then 方法 (onResolved, onRejected) => {}
onResolved函数:成功的回调函数 (value) => {}
onRejected函数:失败的回调函数 (reason) => {}
说明:指定用于得到成功value 的成功回调和用于得到失败reason的失败回调
返回一个新的Promise对象
new Promise((resolve, reject) => { setTimeout(() => { resolve("成功的数据") //reject("失败的数据") },1000) }).then( value => { console.log('onResolved()1',value) } ).catch( reason => { console.log('onRejected()1',reason) } )
3. Promise.prototype.catch 方法 (onRejected)=> {}
onRejected函数:失败的回调函数 (reason) => {}
说明:then()的语法糖,相当于:then(undefined, onRejected) 或 then(null, onRejected)
new Promise((resolve, reject) => {
setTimeout(() => {
//resolve("成功的数据")
reject("失败的数据")
},1000)
}).then(
value => {
console.log('onResolved()1',value)
}
).catch(
reason => {
console.log('onRejected()1',reason)
}
)
4. Promise.prototype.finally 方法
用于指定不管 Promise 对象最后状态如何,都会执行的操作。
- Promise.resolve 方法 : (value) => {}
value:成功的数据或Promise对象
作用:将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去 。
说明:返回一个成功/失败的Promise对象
//产生;一个成功值为1的promise对象
const p1 = new Promise((resolve,reject) => {
resolve(1)
})
//语法糖:就是简介语法
const p2 = Promise.resolve(2)
console.log(p1)
console.log(p2)
6. Promise.reject 方法:(reason) => {}
reason:失败的原因
作用:将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
说明:返回一个失败的Promise对象
//产生;一个失败值为3的promise对象
const p3 = new Promise((resolve,reject) => {
reject(3)
})
const p4 = Promise.reject(3)
console.log('p3:',p3)
console.log('p4:',p4)
//产生;一个成功值为1的promise对象
const p1 = new Promise((resolve,reject) => {
resolve(1)
})
//语法糖:就是简介语法
const p2 = Promise.resolve(2)
console.log('p1:',p1)
console.log('p2:',p2)
const p3 = new Promise((resolve,reject) => {
reject(3)
})
const p4 = Promise.reject(3)
console.log('p3:',p3)
console.log('p4:',p4)
p1.then(value => {console.log(value)})
p2.then(value => {console.log(value)})
p3.then(null,reason => {console.log(reason)})
p4.catch(reason => {console.log(reason)})
7. Promise.all 方法:(promises) => {}
promises:包含n个promise的数组
说明:返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败了就直接失败
const p = Promise.all([p1, p2, p3]);
上面代码中,Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
p
的状态由p1
、p2
、p3
决定,分成两种情况。
(1)只有p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。
(2)只要p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数。
const p1 = new Promise((resolve,reject) => {
resolve(1)
})
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)
const pAll = Promise.all([p1,p2])
console.log('pAll:',pAll)
pAll.then(
value => {
console.log('all onResolved()',value)
},
reason => {
console.log('all onRejected()',reason)
}
)
const p1 = new Promise((resolve,reject) => {
resolve(1)
})
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)
//const pAll = Promise.all([p1,p2])
const pAll = Promise.all([p1,p2,p3])
console.log('pAll:',pAll)
pAll.then(
value => {
console.log('all onResolved()',value)
},
reason => {
console.log('all onRejected()',reason)
}
)
8. Promise.race 方法 (promises) => {}
promises:包含n个promise的数组
说明:返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态
const p = Promise.race([p1, p2, p3]);
上面代码中,只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数。
const p1 = new Promise((resolve,reject) => {
resolve(1)
})
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)
const pRace = Promise.race([p1,p2,p3])
console.log('pRace',pRace)
pRace.then(
value => {
console.log('race onResolved()',value)
},
reason => {
console.log('race onRejected()',reason)
}
)
const p1 = new Promise((resolve,reject) => {
resolve(1)
})
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)
//const pRace = Promise.race([p1,p2,p3])
const pRace = Promise.race([p3,p1,p2])
console.log('pRace',pRace)
pRace.then(
value => {
console.log('race onResolved()',value)
},
reason => {
console.log('race onRejected()',reason)
}
)
状态也不一定是由第一个决定,要看谁先完成,状态就为谁的状态
const p1 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve(1)
},100)
})
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)
const pRace = Promise.race([p1,p2,p3])
console.log('pRace',pRace)
pRace.then(
value => {
console.log('race onResolved()',value)
},
reason => {
console.log('race onRejected()',reason)
}
)
Promise的几大关键问题
-
如何改变Promise的状态?
(1)resolve(value):如果当前是 pending 就会变成 fulfilled
(2)reject(reason):如果当前是 pending 就会变成 rejected
(3)抛出异常:如果当前是 pending 就会变成 rejected
const p = new Promise((resolve,reject) => { //resolve(1) //Promise变成 fulfilled 成功状态 //reject(2) //Promise变成 rejected 失败状态 //throw new Error('出错了') //抛出异常,promise变为rejected失败状态,reason为 抛出的error throw 3 //抛出异常,promise变为rejected失败状态,reason为 抛出的3 }) p.then( value => {}, reason => {console.log('reason',reason)} )
一个 Promise 指定多个成功/失败回调函数,都会调用吗?
当 Promise 改变为对应状态时都会调用
const p = new Promise((resolve,reject) => {
//resolve(1) //Promise变成 fulfilled 成功状态
//reject(2) //Promise变成 rejected 失败状态
//throw new Error('出错了') //抛出异常,promise变为rejected失败状态,reason为 抛出的error
throw 3 //抛出异常,promise变为rejected失败状态,reason为 抛出的3
})
p.then(
value => {},
reason => {console.log('reason',reason)}
)
p.then(
value => {},
reason => {console.log('reason2',reason)}
)
-
改变 Promise 状态和指定回调函数谁先谁后?
(1)都有可能,正常情况下是先指定回调再改变状态,但也可以先改状态再指定回调
(2)如何先改状态再指定回调?
1)在执行器中直接调用 resolve()/reject()
2)延迟更长时间才调用 then()
(3)什么时候才能得到数据?
1)如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
2)如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据
//常规:先指定回调函数,后改变的状态 new Promise((resolve,reject) => { setTimeout(() => { resolve(1) //后改变的状态(同时指定数据),异步执行回调函数 },1000) }).then(//先指定回调函数,保存当前指定的回调函数 value => {console.log('value',value)}, reason => {console.log('reason',reason)} ) /*++++++++++++++++++++++++*/ //如何先改状态,后指定回调函数 new Promise((resolve,reject) => { resolve(1) //先改变的状态(同时指定数据) }).then(//后指定回调函数,异步执行回调函数 value => {console.log('value2',value)}, reason => {console.log('reason2',reason)} ) /*++++++++++++++++++++++++*/ const p = new Promise((resolve,reject) => { setTimeout(() => { resolve(1) //先改变的状态(同时指定数据),异步执行回调函数 },1000) }) setTimeout(() => { p.then( value => {console.log('value3',value)}, reason => {console.log('reason3',reason)} ) },1100)
-
Promise.then()返回的新的Promise的结果状态由什么决定?
(1)简单表达:由then()指定的回调函数执行的结果决定
(2)详细表达
1)如果抛出异常,新的Promise 变为rejected,reason 为抛出的异常
2)如果返回的是非 Promise的任意值,新的Promise变为resolved,value为返回的值
3)如果返回的是另一个新的Promise,此Promise的结果就会成为新的Promise的结果
new Promise((resolve,reject) =>{ resolve(1) }).then( value => { console.log('onResolved1()',value) }, reason => { console.log('onRejected1()',reason) }).then( value => { console.log('onResolved2()',value) }, reason => { console.log('onRejected2()',reason) } )
new Promise((resolve,reject) =>{
resolve(1)
//reject(1)
}).then(
value => {
console.log('onResolved1()',value)
// return 2
// return Promise.resolve(3)
return Promise.reject(5)
},
reason => {
console.log('onRejected1()',reason)
}).then(
value => {
console.log('onResolved2()',value)
},
reason => {
console.log('onRejected2()',reason)
}
)
5. Promise如何串连多个操作任务?
(1)Promise 的then()返回一个新的 Promise,可以开成then()的链式调用
(2)通过then的链式调用串连多个同步/异步任务
new Promise((resolve,reject) =>{
setTimeout(() => {
console.log("执行任务1(异步)")
resolve(1)
},1000)
}).then(
value => {
console.log('任务1的结果:',value)
console.log('执行任务2(同步)')
return 2
}
).then(
value => {
console.log('任务2的结果:',value)
return new Promise((resolve,reject) => {
//启动异步任务3(异步)
setTimeout(() => {
console.log('执行任务3(异步)')
resolve(3)
},1000)
})
}
).then(
value => {
console.log('任务3的结果:',value)
}
)
Promise异常传/穿透?
(1)当使用Promise的then链式调用时,可以在最后指定失败的回调
(2)前面任何操作出了异常,都会传到最后失败的回调中处理
new Promise((resolve,reject) =>{
//resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()',value)
return 2
},
//reason => {throw reason}
).then(
value => {
console.log('onResolved2()',value)
return 3
},
//reason => {throw reason}
).then(
value => {
console.log('onResolved3()',value)
},
reason => Promise.reject(reason)
).catch(reason => {
console.log('onRejected1()',reason)
})
执行reject(1)这个函数,因为第一个then()里面没有reason函数,其实默认为reason => {throw reason},所以会一直往下,然后到第二个then(),第二个也没有reason函数,所有到第三个then(),第三个也没有,所有到最后的catch,这就是传/穿透。
6. 中断Promise链?
(1)当使用Promise的then链式调用时,在中间中断,不再调用后面的回调函数
(2)办法:在回调函数中返回一个pending状态的Promise对象
new Promise((resolve,reject) =>{
//resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()',value)
return 2
},
//reason => {throw reason}
).then(
value => {
console.log('onResolved2()',value)
return 3
},
reason => {throw reason}
).then(
value => {
console.log('onResolved3()',value)
},
reason => Promise.reject(reason)
).catch(reason => {
console.log('onRejected1()',reason)
//throw reason
//return Promise.reject(reason)
return new Promise(() => {})//返回一个pending的Promise 中断Promise链
}).then(
value => {
console.log('onResolved3()',value)
},
reason => {
console.log('onRejected2()',reason)
}
)
如果不想执行catch后面的then了,则用 return new Promise(() => {})中断Promise链,因为此时的状态为pending,既不是fulfilled状态也不是rejected。
async 与 await
async 函数
- 函数的返回值为Promise对象
- Promise对象的结果由async函数执行的返回值决定
// async 函数的返回值是一个Promise对象
async function fn1() {
return 1
}
const result = fn1()
console.log(result)
await 表达式
- await 右侧的表达式一般为 Promise对象,但也可以是其它的值
- 如果表达式是Promise对象,await返回的是Promise成功的值
- 如果表达式是其它值,直接将此值作为await的返回值
function fn2() {
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve(5)
},1000)
})
}
async function fn3() {
const value = await fn2() //await右侧表达式为Promise,得到的结果就是Promise成功的value
console.log('value',value)
}
fn3()
function fn5() {
return 9
}
async function fn3() {
//const value = await fn2() //await右侧表达式为Promise,得到的结果就是Promise成功的value
const value = await fn5() //await右侧表达式不是Promise,得到的结果就是它本身
console.log('value',value)
}
fn3()
注意:
- await必须写在async函数中,但async函数中可以没有await
- 如果await的Promise失败了,就会抛出异常,需要通过try…catch来捕获处理
function fn2() {
return new Promise((resolve,reject) => {
setTimeout(() => {
//resolve(5)
reject(6)
},1000)
})
}
async function fn3() {
try{
const value = await fn2()
console.log('value',value)
} catch (error) {
console.log('得到失败的结果',error)
}
}
fn3()