promise是ES6异步编程新的解决方案,它可以用来解决回调地狱的问题,回调地狱就是许多回调函数嵌套,导致代码太乱了,不易读,也不太好看。如下代码(读文件是异步操作,需要回调函数不停嵌套,透露出一股恶心劲)
fs.readFile('./1.html',(err,data)=>{ if(err) throw err; fs.readFile('./2.html',(err,data1)=>{ if(err) throw err; fs.readFile('./3.html',(err,data2)=>{ if(err) throw err; fs.readFile('./4.html',) }) }) })
promise其实就是一个构造函数,它里面包含一些属性和方法,支持链式调用。
promise其中一个属性PromiseState代表它的状态,它只有三种状态:
fulfilled/resolved(成功)
rejected (失败)
pending (进行中,最初的状态)
其中只会有两种情况的状态变化:
pending -> fulfilled/resolved
pending->rejected
就是只能从初始变为成功,或者从初始变为失败,成功和失败之间不能再互相转化了。
下面我们通过具体的使用来了解。
上代码(最简单的promise使用,定义一个成功的promise)
let p = new Promise((resolve,reject)=>{ resolve('ok') }) console.log(p);
输出P是一个promise实例,它的状态是成功的,PromiseResult是promise的另一个属性,代表promise的值。
定义一个失败的promise
let p = new Promise((resolve,reject)=>{ reject('fail') }) console.log(p);
输出结果是P是一个promise但他的状态是rejected失败的,promise的值就是传入的值
then方法的参数可以传入两个回调函数,当promise状态变为成功( fulfilled)时,执行第一个参数接受的回调函数,状态变为失败(rejected)执行第二个参数接收的回调函数,注意then方法返回的仍然是一个promise对象,所以在then方法之后可以继续调用then方法
上代码
let p = new Promise((resolve,reject)=>{ reject('fail') }) p.then((value)=>{ console.log('成功',value); },(reason)=>{ console.log('失败',reason); })
当一个rejected状态的promise调用.then时执行的是第二个参数的回调函数,所以输出失败,值就是通过reject()传入的值
let p = new Promise((resolve,reject)=>{ resolve('ok') }) p.then((value)=>{ console.log('成功',value); },(reason)=>{ console.log('失败',reason); })
同理,当一个resolve状态的promise调用.then时,执行的是第一个参数的回调函数,输出成功,value值就是通过resolve()传入的值
那我们说then返回的也是一个promise对象,若then里面的回调函数返回的是非promise的数值那么then返回的就是resolved/fulfilled状态的promise,
如果then里面的回调函数返回的是promise类型,则回调函数返回的promise的成功与否直接决定了then方法返回的promise的结果和状态(有些绕,看代码就清楚了)
上代码(回调函数返回非promise)
let p = new Promise((resolve,reject)=>{ resolve('ok') }) const res = p.then((value)=>{ console.log('成功',value); },(reason)=>{ console.log('失败',reason);}) console.log(res);
p是resolved状态的,那么执行then里面的第一个回调函数,打印成功,回调函数并没有返回一个新的promise,因此p.then返回的就是一个状态为fulfilled的promise,值为undifined,因为没有新的promise对象。
回调函数返回promise
let p = new Promise((resolve,reject)=>{ resolve('ok') }) const res = p.then((value)=>{ return new Promise((resolve,reject)=>{ resolve('new Promise') }) },(reason)=>{ console.log('失败',reason);}) console.log(res);
因为成功的回调函数返回了一个新的promise,他决定了then返回的promise的状态和值也是成功的,值就是传入的新值
let p = new Promise((resolve,reject)=>{ resolve('ok') }) const res = p.then((value)=>{ return new Promise((resolve,reject)=>{ reject('fail Promise') }) },(reason)=>{ console.log('失败',reason);}) console.log(res);
若回调函数返回的是失败的promise,则then返回的也是失败的promise,值就是传入的值
promise还有一个特点是可以进行异常穿透,就是已经失败了,就不要总是定义失败(reject)的回调函数了,只要在最后指定一个失败的回调catch()就好了,捕获失败结果,返回值就第一个失败的值,这样代码就更清晰了。
let p1 = new Promise((resolve,reject)=>{ reject('fail') }) p1.then((value)=>{ console.log('成功',value); }).catch(e=> { console.log('失败',e); })
失败的promis执行catch(),then方法里面不用再写失败的回调了
如果.then后面还有.then,许许多多.then也不怕,catch仍然能抓到第一个失败的值
let p1 = new Promise((resolve,reject)=>{ resolve('ok') }) p1.then(value=>{ console.log('111'); }).then(value=>{ console.log('222'); throw('ERROR') }).then(value=>{ console.log('333'); }).catch(reason=>{ console.log('失败',reason) })
成功的promise会继续执行.then方法,直到catch捕获到了第一个失败,就不再继续执行了,不会输出33333
promise.all传入的是一个promise类型的数组,它的返回值也是一个promise,他的状态由数组决定,若promise数组里均为resolved状态的,那么promise.all返回的就是resolved状态的,值就是由所有传入值组成的数组,若promise数组中有一个失败了,那么promise.all返回的就是reject状态的,值就是失败的传入的值
上代码(promise全为成功状态)
let p1 = new Promise((resolve,reject)=>{ resolve('ok') }) let p2 = new Promise((resolve,reject)=>{ resolve('success') }) let p3 = new Promise((resolve,reject)=>{ resolve('nice') }) const res = Promise.all([p1,p2,p3]) console.log(res);
有一个为失败状态
let p1 = new Promise((resolve,reject)=>{ resolve('ok') }) let p2 = new Promise((resolve,reject)=>{ reject('fail') }) let p3 = new Promise((resolve,reject)=>{ resolve('nice') }) const res = Promise.all([p1,p2,p3]) console.log(res);
promise.all传入的也是一个primise数组,返回值是一个promise,只是它的状态和值是由最先发生变化的promise状态和值决定的,就像赛跑先到先得
let p1 = new Promise((resolve,reject)=>{ resolve('ok') }) let p2 = new Promise((resolve,reject)=>{ reject('fail') }) let p3 = new Promise((resolve,reject)=>{ resolve('nice') }) const res = Promise.race([p1,p2,p3]) console.log(res);
P1先到,是成功的,值是ok,则all方法返回的promise是成功的值为ok
若P1变为异步了,则p2先到那么结果一定会是一个返回失败的promise
let p1 = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('ok') }) }) let p2 = new Promise((resolve,reject)=>{ reject('fail') }) let p3 = new Promise((resolve,reject)=>{ resolve('nice') }) const res = Promise.race([p1,p2,p3]) console.log(res);
async是一个函数,返回结果是一个promise对象
1.如果函数里面return的不是一个promise,那promise的结果就是成功的,值就是return的数值
2.如果函数里面return的是一个promise,那async的Promise就是return的promise
3.抛出异常,返回的就是失败的promise对象
(这个很像对于then方法那个表述)
await右边表达式一般为promise对象
1.右边是promise对象,await返回值是promise成功值
2.右边是其他值,直接将此值作为await的返回值(很少用)
注意二者常常结合使用简化promise操作
await必须写在async里面,但是async可以没有await
上代码
async function main(){ let p = new Promise((resolve,reject)=>{ resolve('ok') // reject('error') }) try{ let res = await p; console.log(res); }catch(e){ console.log(e); } } main()
await返回的是promise的成功值,所以需要用try catch捕获失败的情况,输出ok,以后就可以用这样的方法简化promise操作,只需要awaitpromise成功的值即可