ES6 Promise入门

Promise


概念

  • Promise简单来说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise是一个对象,从它可以获取异步操作的消息

特点

  • Promise对象的状态不受外界影响.Promise对象代表一个异步操作,有三种状态:pending(进行中),fulfilled(已成功),rejected(已失败).只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态
  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果.Promise对象的状态改变只有两种可能:从pending变为fulfilled和从pending变为rejected.只要这两种情况发生,状态就不会再变了,会一直保持这个结果,这时就称为resolved(已定型).如果改变已经发生了,再对Promise对象添加回调函数,也会立即得到这个结果.这与事件不同,事件一旦错过了再去监听是不能得到结果的

存在的缺点

  • 无法取消,一旦创建就会立即执行,无法中途取消
  • 如果不设置回调函数,Promise内部抛出的错误不会反应到外部
  • 当处于pending状态时,无法得知目前处于哪一个阶段

基本用法

  • ES6规定,Promise对象是一个构造函数,用来生成Promise实例

      var promise=new Promise(function(resolve,reject){
      	if(/*异步操作成功*/){
      		resolve(value);	//resolve将Promise对象从pending状态变为resolve,在异步成功时调用,将异步操作的结果作为参数传递出去
      	}else{
      		reject(error);	//reject将Promise对象从pending状态变为rejected,在异步失败时调用,将异步操作的错误作为参数传递出去
      	}
      })
    
  • 当Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数

      promise.then(function(value){
      	//success code ...
      },function(err){	//第二个函数参数可选
      	//error code ...    
      })
    
  • 调用resolve或reject并不会导致Promise的参数函数的执行,也就是说即使是已经通过resolve()返回了结果,依然可以执行下面的代码

  • Promise实例具有then方法,定义在Promise.prototype上.作用就是为Promise实例添加状态改变时的回调函数,返回的是一个新Promise实例(不是原来的Promise实例).因此可以采用链式写法

  • Promise.prototype.catch()方法是.then(null,rejection)的别名,用于指定发生错误时的回调函数.此外,then方法指定的回调函数如果运行中抛出错误,也会被catch方法捕获.

    • 如果Promise状态已经变为resolved,再抛出错误是无效的

    • Promise对象的错误具有冒泡性质,会一直向后传递,直到被捕获为止.也就是说错误总会被下一个catch语句捕获

        getJSON('/post/1.json').then(function(post) {
        	return getJSON(post.commentURL);
        }).then(function(comments) {
        	// some code
        }).catch(function(error) {
        	// 处理前面三个Promise产生的错误,分别是getJSON和两个then
        });
      
    • 一般来说,不要使用then里使用第二个reject状态的回调函数,使用catch最好

    • 注意:catch方法返回的还是一个Promise对象,因此后面还可以接着调用then方法.如果没有报错,会跳过catch方法

  • Promise.all方法用于将多个Promise实例包装成一个新的Promise实例:var p=Promise.all([p1,p2,p3]),如果数组中的元素不是Promise实例,会通过Promise.resolve方法将其转为Promise实例,再进一步处理(Promise.all方法的参数可以不是数组,但必须有iterator接口,且返回的每个成员都是Promise实例).p的状态有两种

    1. p1,p2,p3的状态都变为fulfilled,p的状态才会变为fulfilled,此时p1,p2,p3的返回值组成一个数组,传递给p的回调函数
    2. 只要p1,p2,p3中有一个被rejected,p的状态就变为rejected,此时第一个被reject的实例的返回值会传递给p的回调函数
  • Promise.race方法同样是将多个Promise实例包装成一个新的Promise实例:var p=Promise.race([p1,p2,p3]).只要p1,p2,p3之中有一个实例率先改变状态,p的状态就会跟着改变,那个率先改变的Promise实例的返回值就传递给p的回调函数.如果数组中的元素不是Promise实例,会通过Promise.resolve方法将其转为Promise实例,再进一步处理

  • Promise.resolve:将现有对象转为Promise对象.参数有四种情况

    1. 参数是一个Promise实例,不作任何修改,原封不动的返回这个实例

    2. 参数是一个thenable对象(具有then方法的对象),将这个对象转为Promise对象,然后立即执行thenable对象的then方法

       let thenable = {
       	then: function(resolve, reject) {
       		resolve(42);
       	}
       };
       let p1 = Promise.resolve(thenable);
       p1.then(function(value) {
       	console.log(value); // 42
       });
      
    3. 参数不是具有then方法的对象,或根本不是对象.该方法会返回一个新的Promise对象,状态为resolved

    4. 不带有任何参数,直接返回一个resolved状态的Promise对象.注意:立即resolve的Promise对象,是在本轮"事件循环"的结束时,而不是在下一轮"事件循环"的开始时

       setTimeout(function(){	//在下一轮事件循环开始时执行
       	console.log('three');
       },0)
       Promise.resolve().then(function(){	//在本轮事件循环结束时执行
       	console.log('two');
       })
       console.log('one');	//立即执行
       //打印顺序为:one two three
      
  • Promise.reject(reason)方法会返回一个新的Promise实例,该实例的状态为rejected,回调函数会立即执行.注意:Promise.reject()方法的参数,会原封不动的作为reject的理由,变成后续方法的参数

      const thenable = {
      	then(resolve, reject) {
      		reject('出错了');
      	}
      };
      Promise.reject(thenable)
      .catch(e => {
      	console.log(e === thenable)	//Promise.reject(thenable)的参数thenable整体作为catch的参数e,也就是原封不动作为reject的理由
      })
      // true
    
  • done():Promise对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误可能无法捕捉到(因为Promise内部的错误不会冒泡到全局),因此可以提供一个done方法总是处于会掉链的尾端,保证抛出任何可能出现的错误

     asyncFunc()
     	.then(f1)
     	.catch(r1)
     	.then(f2)
     	.done();
     //下面实现done方法
     Promise.prototype.done=function(onFulfilled, onRejected){
     	this.then(onFulfilled, onRejected)	//可以像then方法一样,提供onFulfilled, onRejected状态的回调函数
     		.catch(function(reason){
     			setTimeout(() => {throw reason},0)
     		})
     }
    
  • finally():用于指定不管Promise对象最后状态如何都会执行的操作.它与done方法的最大区别是,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行

Promise.try

  • 实际开发中会遇到一种情况,不知道或者不想区分函数f是同步函数还是异步函数,但都想用Promise来处理.因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误.一般采用如下写法:Promise.resolve().then(f).但是如果这样写的话,它会在本轮事件循环结束的末尾执行

      const f = () => console.log('now');
      Promise.resolve().then(f);
      console.log('next');
      // next
      // now
      //也就是说f是同步的,但用Promise包装了以后,就变成异步执行了
    
  • 接下来是两种让同步函数同步执行,异步函数异步执行,并具有统一API的写法

    1. 使用async函数

       const f = () => console.log('now');
       (async () => f())();	
       //立即执行的匿名函数,会立即执行里面的async函数,如果f是异步的,可以用then指定下一步.
       //但是async() = > f() 会吃掉f()抛出的错误,因此要捕获错误要用promise.catch方法
       console.log('next');
       // now
       // next
       //同时处理异步函数的写法
       (async() => f())()
       	.then(...)
       	.catch(...)
      
    2. 使用new Promise()

       const f = () => console.log('now');
       (
       	() => new Promise(
       		resolve => resolve(f())
       	)
       )();
       console.log('next');
       // now
       // next
       		
       第二行开始的一大段代码,可以用Promise.try(f)来替代
      

你可能感兴趣的:(ES6 Promise入门)