Promise基础知识

一. Promise的含义

  • Promise是异步编程的一种解决方案,比传统的解决方案:回调函数和事件更加合理和强大
  • Promise其实就是一个容器,里面保存着将来才会结束的某个事件(一般是异步操做)
  • Promise对象有如下两个特色:
  1. 对象的状态不受外界影响。Promise对象表明的是异步操做,有三种状态,pending进行中,fulfilled已成功,rejected已失败
  • 只有异步操做的结果才能改变状态,这也是Promise承诺,名字的由来
  1. 一旦状态改变就不会再改变,任什么时候候均可以获得这个结果。
  • Promise对象的状态改变状况:(1) pending改变为fulfilled;pending改变为rejected;
  • 只要改变为fulfilled或者rejected,那么状态就凝固了,不会再改变,此时被称为resolved已定型
  • 可是通常resolved都是特指fulfilled状态,而不包括rejected状态
  1. Promise的一些缺点: (1) 没法取消Promise,一旦新建就会当即执行,没法中途取消 (2) 若是不设置回调函数,那么Promise内部抛出的错误不会反映到外部 (3) 当处于pending状态时,没法得知目前发展到哪一个阶段刚刚开始仍是即将完成

二. 基本用法

1. 基本含义

  • ES6规定,Promise对象是一个构造函数,能够用来生成Promise实例,new Promise()的参数函数中有两个参数,调用第一个参数函数会改变状态为resolved状态,调用第二个会改变状态为rejected状态
  • Promise实例生成以后,可使用then方法指定resolved,rejected状态的回调函数,第一个参数函数在resolved状态时调用,第二个参数在rejected状态时调用
  • 下面是一个Promise实例:
var p=new Promise(function(res,rej){
 console.log("刚刚new Promise()就会执行,即便是赋值操做")  // res();// 状态改成resolved  rej();// 状态改成rejected  })  // 获取状态  p.then(()=>{  console.log('状态改变为resolved')  },()=>{  console.log('状态改变为rejected')  }) 复制代码

2. setTimeout和promise实例

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后执行")  }) 复制代码

3. Promise异步结合同步实例

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); 复制代码

4. ajax请求实例

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) }) 复制代码

5. resolve返回一个Promise实例

  • 若是调用resolve函数或者reject函数返回时带有参数。
  • 那么若是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,当即执行  }) 复制代码

6. resolve以后的同步代码执行

  • 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  */ 复制代码

7.改变状态后即刻结束

// 若是想要在改变状态为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方法是定义在原型对象Promise.prototype上的,做用是为Promise实例添加状态改变时的回调函数
  • then方法的参数不是函数时会发生穿透。也就是then(1).then((data)=>{}),第二个then得到的参数函数的data就是上次穿透的数据1
  • 若是then方法的参数是函数,那么第一个参数是resolved函数,第二个参数是rejected函数
  • 若是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  */ 复制代码
  • 若是then返回的不是Promise实例对象,那么会发生值穿透
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   */ 复制代码

四. catch()

1. 基本含义

  • Promise.prototype.catch()方法相等于Promise.prototype.then(null,reject())或者Promise.prototype.then(undefined,reject())
  • 用于指定发生错误时的回调函数。在异步操做抛出错误时,状态变为rejected,就会调用catch方法
  • 另外在then方法指定的回调函数中抛出错误,就会被catch方法捕获
  • 调用完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  */ 复制代码

2. catch()至关于调用reject

  • 在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 复制代码

3.resolve状态再抛出

  • 因为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  }) 复制代码

4. catch与then第二个参数

  • then处于catch以前,则在catch以前就捕获了错误
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  */ 复制代码

5. Promise内部抛出的错误不会影响到外部

// 1. new Promise内部会抛出错误
 new Promise(function(res,rej){  throw new Error("err")  })  // 2.即便上面抛出了错误,剩余的代码依旧会执行  // 也就是Promise内部抛出的错误不会影响到外部  setTimeout(()=>{  console.log("继续执行")  },1000)  // 会打印错误,以后打印 "继续执行" 复制代码

6. try-catch捕获错误

  • try-catch捕获错误分为: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内部错误  } 复制代码

7. catch返回的是Promise实例

  • 因为catch返回的是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  */ 复制代码

五. finally

  • ES2018才引入的,就是无论promise的状态是什么,最后都会执行
  • finally方法的回调函数不能接受任何参数,这就意味着不能知道前面的Promise状态究竟是fulfilled仍是rejected
  • 因此finally方法里面的操做,应该是和状态无关的,不依赖于Promise的执行结果
  • finally方法的本质上就是在promise实例最后加上一个then来接收resolved和rejected两种状态,而加上finally的好处就在于只须要写一个接收函数就好了,不须要知道状态
  • 而且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()

  • Promise.all()方法用于将多个Promise实例包装成一个新的Promise实例。
  • 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来捕获错误,由于实例离得近  */ 复制代码

多个任务异常

  • 使用promise.all时存在多个异步任务异常,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

七. promise.race()

  • race方法也是接收一组异步任务,而后并行执行异步任务,可是只保留第一个执行结果,剩下的异步任务仍在执行,可是执行结果没法获取
  • promise.race虽然只会接收第一个任务的结果,可是其余异步任务依旧会执行
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 复制代码

八. Promise.resolve()

  • 将现有的对象转换为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"} 复制代码

四种参数状况

  1. 参数为Promise实例,那么就至关于Promise.resolve啥都没作,无效
  • 原封不动的返回原来的Promise实例
  1. 参数是一个thenable对象
  • thenable对象指的是具备then方法的对象
  • Promise.resolve方法会把thenable对象的then方法执行,而后resolve方法的函数参数就是then中传递的参数
  • 注意thenable对象须要有参数res/rej,而后执行res(xxx)用于传递数据
var a={
 then:function(res){  res(444)  }  }  Promise.resolve(a).then((data)=>{  console.log(data);//444  }) 复制代码
  1. 参数是没有then方法的对象或者不是对象
  • 此时直接传递该参数
// 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"}  }) 复制代码
  1. 不带参数
  • 不带参数就then接收不到数据,仍是可使用!

九. Promise.reject()

  • Promise.reject()方法会返回一个新的Promise实例,实例状态为rejected
  • 与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  }) 复制代码

十. 应用

1. 手写promise

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); 复制代码

2. 预加载图片

// 用于预加载图片
 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")) 复制代码
  • 能够在NetWork中看到成功加载图片

本文使用 mdnice 排版html

相关文章
相关标签/搜索