解读ES6之Promise的使用

Promise 是ES6中的异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大,所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理(因为js是单线程的,那我们要解决线程阻塞的问题则必须采用异步操作)

promise对象有两个特点:

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

首先,我们来看一道面试经常遇到关于promise非常经典的问题,下面代码输出的顺序是什么:

setTimeout(function() {
      console.log(1)
    }, 0);
    new Promise(function executor(resolve) {
      console.log(2);
      for( var i=0 ; i<10000 ; i++ ) {
        i == 9999 && resolve();
      }
      console.log(3);
    }).then(function() {
      console.log(4);
    });
    console.log(5);

很显然,答案就是:// 2,3,5,4,1,我们可以这么理解,延时器和promise同为异步操作,但延时器毫无疑问是最后执行的,然后第二慢执行的就是promise对象then方法中的异步操作,所以按顺序执行,可以得到,2,3,5,4,1的答案。

然后,我们来详细讲讲promise中的一些方法:

Promise.prototype.then()

Promise.prototype.catch()

Promise.prototype.finally()

Promise.all()

扫描二维码关注公众号,回复: 2695172 查看本文章

Promise.race()

Promise.resolve()

Promise.reject(

Promise.try()

.then()方法:

简单来讲,then 方法就是把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。

而 Promise 的优势就在于这个链式调用。我们可以在 then 方法中继续写 Promise 对象并返回,然后继续调用 then 来进行回调操作,这样比起原来回调处理异步操作则优雅的多,也便于维护。

我们从举例子来说明这些方法怎么用:先定义几个方法

//做饭
function cook(){
  console.log('开始做饭。');
  var p = new Promise(function(resolve, reject){        //做一些异步操作
  setTimeout(function(){
    console.log('做完饭!');
    resolve('辣椒炒肉');
  }, 1000);
  });
     return p;
} 
//吃饭
function eat(data){
console.log('开始吃饭:' + data);
var p = new Promise(function(resolve, reject){        //做一些异步操作
	setTimeout(function(){
		 console.log('吃完饭!');
		 resolve('一块碗和一双筷子');
	 }, 2000);
	 });
  return p;
} 
 //洗碗
function wash(data){
 console.log('开始洗碗:' + data);
var p = new Promise(function(resolve, reject){        //做一些异步操作
	setTimeout(function(){
		 console.log('洗完碗!');
		 resolve('干净的碗筷');
	}, 2000);
	 });
	 return p;
}

当我们需要按一定循序做这些事情的时候,我们则可以用.then()的链式语法来调用这些方法:

 cook()
 .then(function(data){
	 return eat(data);
  })
 .then(function(data){
	 return wash(data);
 })
.then(function(data){
  console.log(data);
});

 我们可以得到如下结果:                开始做饭。
promise3.html?__hbt=1533796225113:14 做完饭!
promise3.html?__hbt=1533796225113:22 开始吃饭:辣椒炒肉
promise3.html?__hbt=1533796225113:25 吃完饭!
promise3.html?__hbt=1533796225113:33 开始洗碗:一块碗和一双筷子
promise3.html?__hbt=1533796225113:36 洗完碗!
promise3.html?__hbt=1533796225113:51 干净的碗筷

当然我们也可以用这样的简写方法调用,但会有点问题,后面我们会讲到:

cook()
.then(eat)
.then(wash)
.then(function(data){
    console.log(data);
});

reject()方法:

上面样例我们通过 resolve 方法把 Promise 的状态置为完成态(Resolved),这时 then 方法就能捕捉到变化,并执行“成功”情况的回调。而 reject 方法就是把 Promise 的状态置为已失败(Rejected),这时 then 方法执行“失败”情况的回调(then 方法的第二参数)。

//做饭
function cook(){
	console.log('开始做饭。');
	var p = new Promise(function(resolve, reject){        //做一些异步操作
	setTimeout(function(){
		 console.log('做完饭!');
		 reject('胡了的米饭');
	 }, 1000);
 });
 return p;
} 
//吃饭
function eat(data){
	console.log('开始吃饭:' + data);
	var p = new Promise(function(resolve, reject){        //做一些异步操作
	setTimeout(function(){
	console.log('吃完饭!');
	resolve('一块碗和一双筷子');
	}, 2000);
});
	return p;
} 

然后我们调用并得到结果:

//	cook()	
//	.then(eat,function(data){
//	console.log(data+"没法吃")
//	})
// 这两种调用方法是一样的
	cook()
    .catch(function(err){
		console.log(err+"没法吃")
	})
结果:

开始做饭。
promise3.html?__hbt=1533796225113:14 做完饭!
promise3.html?__hbt=1533796225113:59 胡了的米饭没法吃

如果我们只处理失败的结果,则可以使用:

.then(null,function(){})或者是使用后面的.catch()

.catch()方法:

cook()
				.catch(function(err){
					console.log(err+"没法吃")
				})

当我们在.then()方法中任意位置抛出任意错误,我们都可以在.catch()中捕获并进行处理

all()方法:

Promise 的 all 方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调,两个个异步操作是并行执行的,等到它们都执行完后才会进到 then 里面。同时 all 会把所有异步操作的结果放进一个数组中传给 then

我们同样拿上面吃饭和做饭的栗子:

 Promise
 .all([cook(),eat()])
 .then(function(res){
     console.log("a开始执行")
     console.log(res)
   })
结果:
开始做饭。
promise3.html?__hbt=1533796225113:22 开始吃饭:undefined
promise3.html?__hbt=1533796225113:14 做完饭!
promise3.html?__hbt=1533796225113:25 吃完饭!
promise3.html?__hbt=1533796225113:64 a开始执行
promise3.html?__hbt=1533796225113:65 (2) ["胡了的米饭", "一块碗和一双筷子"]

race()方法:

顾名思义,就是赛跑的意思,all()方法是灯所有方法执行在执行.then()里面的语句,而race()是只要有一个异步操作执行完成就会执行.then()里面的回调,但没有执行完的异步操作也会继续执行,而不是停止。

比如我们给一个请求设置超时时间:

//请求某个图片资源
function requestImg(){
    var p = new Promise(function(resolve, reject){
    var img = new Image();
    img.onload = function(){
       resolve(img);
    }
    img.src = 'xxxxxx';
    });
    return p;
}
 
//延时函数,用于给请求计时
function timeout(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            reject('图片请求超时');
        }, 5000);
    });
    return p;
}
 
Promise
.race([requestImg(), timeout()])
.then(function(results){
    console.log(results);
})
.catch(function(reason){
    console.log(reason);
});

我们把两个方法放在race()中赛跑,如果5秒内请求成功,则正常进入.then()方法中,如果5秒内图片还未请求回来,则报请求图片超时。

finally()方法:

不管promise最后的状态,在执行完thencatch指定的回调函数以后,都会执行finally方法指定的回调函数。不接受参数,和promise的执行结果无关

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

try()方法:

有时候不知道或者不想区分,函数f是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误,但是如果是同步函数,放入.then()方法中,则会在本轮事件结尾执行,则try()方法就是让同步函数同步执行,异步函数异步执行。

const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
// next
// now

到此我们就讲完了promise中的几种方法:注意区分Promise的方法和promise实例对象的方法:

let p = new Promise()
p.catch()
p.reject() // 没有这个东西
Promise.reject()
Promise.catch() // 没有这个东西

从而再次区分一下reject()和catch()两种方法:

第一:reject()是属于Promise对象的方法,Promise.reject(),而catch()是属于promise实例的方法,promise.prototype.catch()或者:var p = new Promise(),  p.catch().then()  ;

第二:reject()是抛出异常,而catch()是捕获异常并处理,比如说,我们reject("出异常了"),那我们catch()内就可以捕获到“出异常了”这个信息

最后一个,看过上面链式调用.then()方法的小伙伴不知道有没有发现一个问题:

方式一:
p1().then(p2).then(p3)
  .then(function(data) {
    console.log('data: ' + data);
  })
  .catch(function(error) {
    console.log('error: ' + error);
  });
 
function p1() {
  return new Promise(function(resolve, reject) {
    console.log('p1 resolved');
    resolve(123);
  });
}
 
function p2() {
  return new Promise(function(resolve, reject) {
    console.log('p2 rejected');
    reject(456);
  });
}
 
function p3() {
  return new Promise(function(resolve, reject) {
    console.log('p3 resolved');
    resolve(789);
  });
}

此种方式调用打印结果:p1 resolved  , p2 rejected  , error: 456,并没有打印出p3的结果,这是因为在一个promise链中,只要任何一个promise被reject,promise链就被破坏了,reject之后的promise都不会再执行,而是直接调用.catch方法,所以我们加入.catch()方法则可以捕获这些错误。

我们想要promise链不被破坏,则可以如下调用:(切记:只要返回的promise对象才可以调用promise中对应的方法)

var p1 = new Promise(function(resolve, reject) {
		 console.log("p1")
		 //when do something done
		 resolve();
	});
var p2 = new Promise(function(resolve, reject) {
		 console.log("p2")
		  //when do something done
		  reject(123);
});
var p3 = new Promise(function(resolve, reject) {
		 console.log("p3")
		 //when do something done
		  resolve();
});
p1.then(function() {
   return p2
	})
.then(function() {
	 return p3
  })
.catch(function(err){
	console.log('错误为:',err)
})

打印的结果为:p1  , p2, p3 , 错误为: 123

到此promise中的一些知识点就讲完了,希望能帮助大家,有错误之处欢迎指正,谢谢

猜你喜欢

转载自blog.csdn.net/qq_39009348/article/details/81535543