只有异步操做的结果才能改变状态,这也是Promise承诺,名字的由来
resolved已定型
可是通常resolved都是特指fulfilled状态,而不包括rejected状态
一旦新建就会当即执行
,没法中途取消 (2) 若是不设置回调函数,那么Promise内部抛出的错误不会反映到外部
(3) 当处于pending状态时,没法得知目前发展到哪一个阶段刚刚开始仍是即将完成
new Promise()的参数函数中有两个参数,调用第一个参数函数会改变状态为resolved状态,调用第二个会改变状态为rejected状态
第一个参数函数在resolved状态时调用,第二个参数在rejected状态时调用
var p=new Promise(function(res,rej){ console.log("刚刚new Promise()就会执行,即便是赋值操做") // res();// 状态改成resolved rej();// 状态改成rejected }) // 获取状态 p.then(()=>{ console.log('状态改变为resolved') },()=>{ console.log('状态改变为rejected') }) 复制代码
function pro(time){ return new Promise((res,rej)=>{ // new Promise以后就执行,res会在time时间后执行,从而状态改变为resolved setTimeout(res,time) }) } console.log(Math.floor(new Date().getTime()/1000)) pro(1000).then(()=>{ console.log(Math.floor(new Date().getTime()/1000)) console.log("在1000ms后执行") }) 复制代码
let p=new Promise(function(res,rej){ console.log("new Promise代码执行,会当即执行该步骤") console.log(1); res() }) p.then(()=>{ console.log("then操做时异步操做,会在本轮宏任务结束以前才执行") console.log(3) }) console.log(2); 复制代码
var get=function(url){ return new Promise((res,rej)=>{ var xhr=new XMLHttpRequest(); xhr.open("GET",url); xhr.onreadystatechange=function(){ if(this.readyState==4&&this.status==200){ res(this.response) } } xhr.onerror=(err)=>{ rej(err) } xhr.send(); }) } // http://127.0.0.1:8849,此时会报错(由于本地没开启该端口) get("http://127.0.0.1:8848/MyHxsj/面经.md").then((data)=>{ console.log(data) },(err)=>{ console.log(err) }) 复制代码
resolve函数的参数是一个Promise实例时,返回的实例的状态决定了当前Promise实例的状态
若是返回的Promise实例的状态是pending,那么当前Promise实例会等待pending状态改变
若是返回的Promise实例的状态是resolved或者rejected,那么回调函数就会当即执行
// 1. 返回的Promise实例的状态为pending var p1=new Promise(function(res,rej){ console.log("pending") setTimeout(function(){ res("resolved") },2000) }) var p2=new Promise(function(res,rej){ res(p1);// resolved状态,可是传递的参数为Promise实例 }) p2.then((data)=>{ console.log(data) console.log("在返回的Promise实例p1状态为resolved以后执行") }) /* pending // 两秒以后 resolved 在返回的Promise实例p1状态为resolved以后执行 */ // 2. 返回的Promise实例的状态为resolved var p3=new Promise(function(res){ res("p3") }) var p4=new Promise(function(res,rej){ res(p3) }) p4.then((res)=>{ console.log(res);//p3,当即执行 }) 复制代码
promise实例改变状态为resolve不会改变同步代码的执行
// 1. resolved new Promise((res,rej)=>{ res(1); console.log("此前状态为resolved,可是依旧会执行本行代码") }).then((data)=>{ console.log(data) }) console.log('宏任务代码') /* 此前状态为resolved,可是依旧会执行本行代码 宏任务代码 1 */ // 2. rejected new Promise((res,rej)=>{ rej(new Error("错误error:")); console.log("此时状态为rejected,本行代码会执行") }).then(()=>{},(err)=>{ console.log(err) }) /* 此时状态为rejected,本行代码会执行 // 而后才会轮到微任务执行 Error: 错误error: at 平时测试.html:130 at new Promise (<anonymous>) at 平时测试.html:129 */ 复制代码
// 若是想要在改变状态为resolved或者rejected以后 // 不会继续执行往下代码,那么使用return new Promise(function(res,rej){ return res(1) // return rej(1) console.log("已经return,因此不会继续执行本行代码") }).then((data)=>{ console.log(data) },(err)=>{ console.log(err) }) // 结果只会打印: 1 复制代码
then方法的参数不是函数时会发生穿透。也就是then(1).then((data)=>{}),第二个then得到的参数函数的data就是上次穿透的数据1
若是promise实例存在多个then,那么下一个then会等待上一个then返回的promise实例状态改成resolved/rejected才会执行
// 多个then,须要等待上一个then的状态改成resolved/rejected new Promise((res,rej)=>{ res(1) }).then((res)=>{ console.log(res); return new Promise((res,rej)=>{ setTimeout(res,1000);// 一秒后,状态改成resolve }) }).then(()=>{ console.log(2) }) /* 1 // 1秒后 2 */ 复制代码
new Promise((res,rej)=>{ res(1) }).then(2).then((data)=>{ console.log(data);//1 return 3; }).then((data)=>{ console.log(data)//3 }) /* res(1),函数res传递的是一个number类型的变量 then(2),不是函数,没有传递值,因此无效,仅仅是一个语句 2 // 因此1的值会继续穿透,then(2)无效 1,第二个then属于函数对象,因此可以接收到数据1 return 3,传递数据3 最后一个then,打印3,接收到上个then函数返回的数据3 */ 复制代码
Promise.prototype.catch()方法相等于Promise.prototype.then(null,reject())或者Promise.prototype.then(undefined,reject())
调用完catch方法,状态会变为resolved!
// 1. 捕获Promise错误 new Promise(function(res,rej){ throw new Error("抛出错误") }).catch(function(err){ console.log("err:",err) }) /* err: Error: 抛出错误 at 平时测试.html:116 at new Promise (<anonymous>) at 平时测试.html:115 */ // 2. then方法中抛出错误 new Promise(function(res,rej){ res(1) }).then(function(data){ throw new Error("then err") }).catch(function(err){ console.log(err) }) /* Error: then err at 平时测试.html:131 */ 复制代码
在promise的后面使用catch其实至关于在promise内部加上try-catch语句,或者至关于加上一个reject状态改变(在某个条件符合时)
// 1. catch方法相等于try-catch语句 new Promise(function(res,rej){ try{ throw new Error("try抛出错误") }catch(e){ console.log(e) } }) /* Error: try抛出错误 at 平时测试.html:117 at new Promise (<anonymous>) at 平时测试.html:115 */ // 2. catch方法还相等于调用reject方法而后在then的第二个参数监听 new Promise(function(res,rej){ rej("rej err") }).then(()=>{},(err)=>{ console.log(err) });//rej err 复制代码
因为catch至关于调用rejected状态,而且状态改成rejected后不能够改成resolve状态
因此Promise状态为resolve时,再抛出错误,没法在catch捕获到
// 1. res(),resolve状态后不能够改成reject状态 new Promise(function(res,rej){ res("ok");//状态为resolve throw new Error("此时抛出错误没法被捕获到") }).catch(function(err){ console.log(err) }).then((data)=>{ console.log(data);//ok }) 复制代码
new Promise(function(res,rej){ throw new Error("err") }).then(()=>{},(err)=>{ console.log("reject:",err) }).catch(function(data){ console.log('catch:',data) }) /* reject: Error: err at 平时测试.html:116 at new Promise (<anonymous>) at 平时测试.html:115 */ 复制代码
// 1. new Promise内部会抛出错误 new Promise(function(res,rej){ throw new Error("err") }) // 2.即便上面抛出了错误,剩余的代码依旧会执行 // 也就是Promise内部抛出的错误不会影响到外部 setTimeout(()=>{ console.log("继续执行") },1000) // 会打印错误,以后打印 "继续执行" 复制代码
try执行以前,try执行过程当中,try执行以后
// 1.语法错误,在执行try代码以前就抛出错误,因此catch没法捕获到 /* try{ a. }catch(e){ console.log("myerr:",e) } */ /* Uncaught SyntaxError: Unexpected token '}' */ // 2. 在try代码执行过程当中抛出的错误(能够捕获到) /* function add(x){return x+y}; // 错误函数 try{ console.log(add(1)) }catch(e){ console.log(e);//ReferenceError: y is not defined } */ // 3. 在try代码执行完毕以后抛出的错误(不能捕获) /* try{ setTimeout(function(){ console.log('定时器') // 此时的错误没有被catch捕获到 a.b;//Uncaught ReferenceError: a is not defined },2000) }catch(e){ console.log("捕获到的错误:",e); } */ // 4.try/catch没法捕获到Promise异常时由于Promise内部就捕获到异常了,没有往外抛出异常,因此外部的catch没法捕获到异常 try{ new Promise(function(res,rej){ // 当没有在Promise中设置catch时,提示下面语句 throw new Error("err");//Uncaught SyntaxError: Missing catch or finally after try }).then(()=>{},(err)=>{ console.log("第二个参数:",err) /* 第二个参数: Error: err at 平时测试.html:147 at new Promise (<anonymous>) at 平时测试.html:145 */ }) .catch(function(err){ console.log("catch:",err) /* 没有设置then第二个参数函数时 catch: Error: err at 平时测试.html:147 at new Promise (<anonymous>) at 平时测试.html:145 */ }) }catch(e){ console.log("promise:",e);// 没法捕获到promise内部错误 } 复制代码
因此catch后面接上的then能够继续执行
catch中也能够继续抛出错误,而后被后面的then或者catch继续捕获
// 1. catch捕获抛出的错误后,then方法能够继续执行 new Promise(function(){ throw new Error("err") }).catch(function(err){ console.log(err) }).then(function(){ console.log("catch捕获错误后,还能够继续执行then") }) /* Error: err at 平时测试.html:116 at new Promise (<anonymous>) at 平时测试.html:115 catch捕获错误后,还能够继续执行then */ // 2. catch中也能够抛出错误,而后被下一个catch捕获 new Promise(function(res,rej){ throw new Error("第一个错误") }).catch(function(err){ console.log(err) throw new Error("第二个错误") }).catch(function(err){ console.log(err) }) /* Error: 第一个错误 at 平时测试.html:133 at new Promise (<anonymous>) at 平时测试.html:132 Error: 第二个错误 at 平时测试.html:136 */ 复制代码
ES2018才引入的,就是无论promise的状态是什么,最后都会执行
finally方法的回调函数不能接受任何参数,这就意味着不能知道前面的Promise状态究竟是fulfilled仍是rejected
而且finally方法老是会返回原来的值
// 1. finally在最后 new Promise(function(res,rej){ res(1) }).then((data)=>{ console.log(data);//1 return 2 }).finally(()=>{ console.log(3);//3, finally方法不能接受到参数,因此2接收1不到 }) /* 1 3 */ // 2. finally后面还有then方法 new Promise((res,rej)=>{ res(11) }).then((data)=>{ console.log(data);//11 return 22; }).finally(()=>{ console.log(33);//33 }).then((data)=>{ console.log(data);//22,执行上一个被返回的then }) // 3. finally后面还有then,而且finally返回值 new Promise((res,rej)=>{ res(111) }).then((data)=>{ console.log(data);//111 return "后续then接收到以前的then,而不会接收到finally return的变量" }).finally(()=>{ console.log(222);//222 return 333; }).then((data)=>{ console.log(data);//后续then接收到以前的then,而不会接收到finally return的变量 }) 复制代码
Promise.all()接收一个具备Iterator接口的数据做为参数,关键在于成员必须都是Promise实例
成员都变为resolved以后会返回结果数组;若是有一个成员变为rejected状态,那么返回rejected状态Promise实例的返回值
// 1. 所有resolved var p1=new Promise((res,rej)=>{ res(1); }) var p2=new Promise((res,rej)=>{ res(2); }) var p3=new Promise((res,rej)=>{ res(3); }) // Promise.all()返回的是一个Promise实例 // 该实例的值存储在内部属性 [[PromiseValue]] // 想要获取,须要经过then方法来保存参数 var res1=Promise.all([p1,p2,p3]) .then((data)=>{ console.log(data);//[1, 2, 3] return data; }) console.log(res1);//Promise {<pending>} // 2. 有一个rejected,其余的中止执行 var p4=new Promise((res,rej)=>{ console.log("p4") setTimeout(res(4),5000); }) // 2.1 错误没被捕获 var p5=new Promise((res,rej)=>{ setTimeout(rej("error:"+5555),1000) }) var p6=new Promise((res,rej)=>{ console.log("p6") res(6) }) /* var res2=Promise.all([p4,p5,p6]).then((data)=>{ console.log("all:",data); }) */ // 2.2 /* var p5=new Promise((res,rej)=>{ setTimeout(rej("error:"+5555),1000) }).catch(function(err){ console.log("err",err);//err error:5555 }).then((res)=>{ return "res" }) console.log(p5) */ // 2.3 给Promise.all后面添加catch捕获错误 var res2=Promise.all([p4,p5,p6]).then((data)=>{ console.log("all:",data); }).catch((err)=>{ console.log("all+err",err);//all+err error:5555 }) /* 2.1 没有给p5设置catch捕获错误,因此最后执行错误,Promise.all没接住错误 2.2 给p5设置catch捕获错误,则Promise.all剩余实例也会执行,可是返回rej的实例的值多是undefined 也就是Promise.all的结果是[4,undefined,6] 2.3 给Promise.all后面添加catch捕获错误,打印all+err error:5555 2.4 若是实例和Promise.all都设置了catch,那么使用实例的catch来捕获错误,由于实例离得近 */ 复制代码
只捕获最先的那个错误
function runAsync (x) { const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000)) return p } function runReject (x) { const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x)) return p } Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)]) .then(res =>console.log(res)) .then(res =>console.log(res),rej=> console.log('error:',rej)) .catch(err =>console.log(err)) 复制代码
多个异步任务是并行执行的,哪一个先执行完毕不必定
当all方法出现异常时,all以后的then接收的是reject函数,参数就是第一个异常返回的参数
当all方法以后的then不存在reject函数时,则catch接受异常参数
即便不是all以后的第一个then存在reject函数,只要以后的then存在reject函数,就不会轮到catch去接收异常
若是异步任务中存在报错,那么res不会执行,rej才会执行,也就是报错
若是不存在报错,那么就会执行res
只保留第一个执行结果,剩下的异步任务仍在执行,可是执行结果没法获取
其余异步任务依旧会执行
function runAsync(x) { const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000) ); return p; } function runReject(x) { const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x) ); return p; } /* 首先打印0,而后返回rej('Error:0') 该rej被Promise.race的catch捕获,可是剩下的成员依旧执行 因此runAsync还会打印 1 2 3 */ Promise.race([runReject(0), runAsync(1), runAsync(2), runAsync(3)]) .then(res =>console.log("result: ", res)) .catch(err =>console.log(err));// Error:0 复制代码
将现有的对象转换为resolve状态的异步对象就使用Promise.resolve()
// 1. 使用Promise.resolve() var res1=Promise.resolve("1"); console.log(res1);//Promise {<resolved>: "1"} // 2. 等同于 new Promise((res,rej)=> res()) var res2=new Promise((res,rej)=>{ res("2") }) console.log(res2);//Promise {<resolved>: "2"} 复制代码
那么就至关于Promise.resolve啥都没作,无效
注意thenable对象须要有参数res/rej,而后执行res(xxx)用于传递数据
var a={ then:function(res){ res(444) } } Promise.resolve(a).then((data)=>{ console.log(data);//444 }) 复制代码
此时直接传递该参数
// 1. 参数不是对象 Promise.resolve('hello world').then((data)=>{ console.log(data);//hello world }) // 2. 参数是对象,可是没有then方法 Promise.resolve({a:'hello'}).then((data)=>{ console.log(data);//{a: "hello"} }) 复制代码
不带参数就then接收不到数据,仍是可使用!
与Promise.resolve()不一样之处在于,reject不管参数为何,都是直接返回该参数!
注意要使用catch接收,不然会报错,由于是reject状态
// 1. thenable对象,直接返回参数 var obj={ then:function(res,rej){ rej("err") } } Promise.reject(obj).catch((e)=>{ console.log(e);//{then: ƒ} }) //2. 其余参数 Promise.reject("he").catch((res)=>{ console.log(res);// he }) 复制代码
function mypromise(func){ var that=this; // 回调函数集 that.funclist=[]; // resolve方法 function resolve(value){ // 微任务 setTimeout(()=>{ that.data=value that.funclist.forEach((callback)=>{ callback(value) }) }) } // 执行用户传入的函数 func(resolve.bind(that)) } mypromise.prototype.then=function(onResolved){ var that=this; return new mypromise(resolve => { that.funclist.push(function(){ var res=onResolved(that.data); if(res instanceof mypromise){ res.then(resolve) }else{ resolve(res); } }) }) } const func=resolve => { setTimeout(()=>{ resolve(1) },1000) } var promise1 = new mypromise(func) promise1.then(res => { console.log(res) return new mypromise(resolve => { setTimeout(() => { resolve(2) }, 500) }) }) console.log(promise1); 复制代码
// 用于预加载图片 function loadImg(url){ return new Promise(function(res,rej){ var img=document.createElement('img'); img.src=url; // 设置了src,也就是默认开始请求图片资源了 img.onload=res;//指向函数res img.onerror=rej; }) } console.log(loadImg("http://yiyeblog.com/imgs/ES6.png")) 复制代码
本文使用 mdnice 排版html