ES6的Promise详解

Posted 柒小莫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES6的Promise详解相关的知识,希望对你有一定的参考价值。

文章目录


前言

本篇文章主要介绍了ES6语法中的Promise对象的使用详解,promise对象是JS进阶学习中的重要知识点,
如果本文对你有所帮助请三连支持博主,你的支持是我更新的动力,先赞后看养成习惯。


以下是本篇文章正文内容

一、Promise的概念

Promise是异步编程的一种解决方案,是一个对象,可以获取异步操作的消息,大大改善了异步编程的困难,避免了回调地狱,比传统的解决方案回调函数和事件更合理和更强大。

所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

ES6 开始支持 Promise
Promise 对象用于一个异步操作的最终完成(包括成功和失败)及结果值的表示。简而言之,就是处理异步请求的。之所以叫做 Promise,就是承诺做这件事,如果成功则怎么处理,失败则怎么处理。

Promise 首先是一个对象 (是一个对象!!!!),它通常用于描述现在开始执行,一段时间后才能获得结果的行为(异步行为),内部保存了该异步行为的结果。然后,它还是一个有状态的对象

  • pending:待定
  • fulfilled:兑现,有时候也叫解决(resolved)
  • rejected:拒绝

一个 Promise 只有这 3 种状态,且状态的转换过程有且仅有 2 种

  • pending 到 fulfilled (resolved)
  • pending 到 rejected

二、使用Promise

创建 Promise

调用 Promise 构造函数来创建一个 Promise。

let promise = new Promise((resolve, reject) => // 要做的事情...);

Promise 构造函数接收一个函数作为参数,该函数的两个参数是 resolve,reject,它们由 javascript 引擎提供。

resolve 函数的作用是,将 Promise 对象的状态从“未完成”变为“成功”(即从pending 变为resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject 函数的作用是,将 Promise 对象的状态从“未完成”变为“失败”(即从pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。


Promise 常用方法

Promise.prototype.then()

then 方法可以接收两个回调函数作为参数,第一个回调函数是Promise对象的状态改变为 resoved 是调用,第二个回调函数是 Promise 对象的状态变为 rejected 时调用。其中第二个参数可以省略。

promise.then((resoved)=>
	console.log('成功')
,(rejected)=>
	console.log('失败')
)

Promise.prototype.catch()

该方法相当于 then 方法的第二个参数,指向 reject 的回调函数。
另一个作用是在执行resolve回调函数时,如果出错,抛出异常,不会停止运行,而是进入catch 方法中。

catch 只接受一个参数,也就是 rejected 抛出的值,一般用于异常处理。传统的try/catch捕获不了Promise内部的异常的,因为抛出异常这个动作是异步的。在处理异常的时候,我们可以在catch中进行异常的捕获,也可以直接抛出异常。

p.then((data) => 
     console.log('resolved',data);
,(err) => 
     console.log('rejected',err);
  
); 

p.then((data) => 
    console.log('resolved',data);
).catch((err) => 
    console.log('rejected',err);
);

all()

Promise 的 all 方法提供了并行执行异步操作的能力,在 all 中所有异步操作结束后才执行回调。

function p1() 
  var promise1 = new Promise(function (resolve, reject) 
    console.log("p1的第一条输出语句");
    resolve("p1完成");
  );
  return promise1;


function p2() 
  var promise2 = new Promise(function (resolve, reject) 
    console.log("p2的第一条输出语句");
    setTimeout(() => 
      console.log("p2的第二条输出语句");
      resolve("p2完成");
    , 2000);
  );
  return promise2;


function p3() 
  var promise3 = new Promise(function (resolve, reject) 
    console.log("p3的第一条输出语句");
    resolve("p3完成");
  );
  return promise3;


Promise.all([p1(), p2(), p3()]).then(function (data) 
  console.log(data);
);

输出结果:

p1的第一条输出语句;
p2的第一条输出语句;
p3的第一条输出语句;
p2的第二条输出语句[("p1完成", "p2完成", "p3完成")];

链式调用

链式调用的过程中 then() 方法或 catch() 方法的回调的返回值会作为下一个 then() 方法的回调的参数,无返回值也可继续 .then(),如果没有错误,catch 方法将发生穿透。

new Promise((resolve, reject) => 
        resolve('请求成功了');
      )
        .then((value) => 
          console.log(value);
          return new Error('错误');
        )
        .catch(() => 
          console.log('错误'); //未打印,发生穿透
        )
        .then((value) => 
          console.log(111);
        )
        .then((value) => 
          console.log(222);
        )

详解 ES6 Promise异步

详解ES6 Promise异步



前言

我打算先解释resolve和reject这两个方法, 因为then()和catch()负责接收resolve和reject方法的返回物,这样可能会更好理解些,然后all方法这多少有点规格外,我们最后说.


一、Promise是什么呢…

一种异步编程解决方案,多用于一般很耗时的网络请求(JS是单线程语言,如果使用同步来进行程序,中途用户一旦要进行网络请求,会与同步执行的程序出现冲突,称为"阻塞",线程阻塞进行操作也无法执行);

在执行一些复杂的回调时,使用Promise可以较好的避免回调地狱;

特性:

  1. 本质是构造函数;
  2. 自身带有 resolve | reject | all() 等方法;
  3. 原型中还提供了 then() | catch() 两种方法;

二、resolve

使用Promise的时候需要先作为参数传入,具体见下面例子;

resolve()方法在执行成功后回调Promise,然后拿到Promise返回的数据传给then().

在resolve回调的过程中如果出错,程序不会立即停止运行,而是会将错误报告推送给catch();

它只返回一个数据,就是回调的结果,如果是网络请求的话就是请求结果,在调用到then的时候then里必然就已经有了正确无异常的结果(不然也不会调用then()),于是事先在then()里写处理方法的时候就可以拿一个参数来接收(在进行网络请求的时候一般会用res):

//先传入两个方法;
	promise((resolve, reject) => {
      setTimeout(() => {
      //我们可以手动调用resolve();
        resolve(); 
      }, 1000)
    }).then(
         function(res){
         //使用res来接收resolve返回给then的数据;
		   console.log("回调结果是:" + res);
	     }
       )
	

三、reject

使用Promise的时候需要先作为参数传入,具体见下面例子;

reject()方法在执行失败后回调Promise,然后拿到错误报告传给catch().
在resolve回调的过程中如果出错,程序也会将错误报告推送给catch().

返回错误报告,在调用到catch的时候catch里必然就已经有了错误报告(不然也不会调用catch()),于是事先在catch()里写处理方法的时候就可以拿一个参数来接收(一般会用error或者err):

//先传入两个方法;
	promise((resolve, reject) => {})
	  .then()
      .catch((error) => {
           console.log("出错啦:" + error);
       })
	

四、then() & catch()

我感觉把这两个放在一起说可能会更好理解一些,因为两位的关系是并列的…

then()和catch()可以在一次Promise链式调用中同时使用,then()会负责接收reslove方法返回的数据,而catch()会接收reject方法返回的错误报告;

promise((resolve, reject) => {
  setTimeout(() => {
    resolve() 
  }, 1000)
}).then().catch();	


个人觉得这两个方法成功的将Promise和其他请求方法区分开了,也建立了Promise独有的优势;

依照Promise的思想,其将请求的各个步骤交付专门的方法进行处理:

resolve方法在成功的时候回调Promise并提供数据给then().

reject方法在失败的时候回调Promise并且提供错误报告给catch().

在then()里专门处理数据,而不是拿到后夹在各个请求之间直接原地处理.

就算是报错也有catch()专门负责;


其中then()在优化回调嵌套代码这块算是功不可没了,将嵌套调用转换为链式调用可谓是大大优化了代码可读性,极大程度避免了回调地狱.

啊,这么说似乎不太好直观,我先来模拟一个层层嵌套的回调地狱,然后您和Promise异步方案对比就一目了然了:

//欢迎来到回调地狱 doge)
//我们需要URL1来获取DATA1数据,而下一次请求所需的URL2在DATA1里
//获取DATA2数据又需要URL2,而下一次请求所需的URL3又在DATA2里,
new Promise((resolve, reject) => {
    setTimeout(() => {
    //首层
         console.log(xxx);
         console.log(xxx);
         console.log(xxx);

         setTimeout(() => {
         //倒数第二层回调,需要上一级回调的结果才能执行;
               console.log(xxx);
               console.log(xxx);
               console.log(xxx);

             setTimeout(() => {
             //套在最里面的回调,需要上一级回调的结果才能执行;
                  console.log(xxx);
                  console.log(xxx);
                  console.log(xxx);
             })
         }, 1000)
   }, 1000)
})
//今天写着挺头疼,明天看着更头疼.

差不多就是这么个意思,用setTimeout来模拟回调函数,层层嵌套就跟棕榈柱似的,外层的扒不下来里面的也没法动.
我们再来看看Promise的解决方案:

//全程没有内部嵌套,链式执行;
new Promise((resolve, reject) => {
//第一次回调;
    setTimeout(() => {
      resolve() 
   }, 1000)
}).then(() => {
//then中处理第一次回调结果
         console.log(HelloWorld);
//拿到第一次回调的结果,在第一次回调的then中发起第二次回调;
 return new Promise((resolve, reject) => {
         setTimeout(() => { 
              resolve()
         }, 1000)
     })
}).then(() => {
//处理第二次回调结果
         console.log(HelloVue);
//拿到第二次回调的结果,在第二次回调的then中发起第三次回调;
 return new Promise((resolve, reject) => {
         setTimeout(() => {
              resolve()
         })
     })
}).then(() => {
//处理第三次回调结果;
         console.log(HelloPHP);
    })

写Promise的话代码可能会很长,但是一定能让你避免队友的hammer of justice.


五、all

该方法针对并发的异步操作,在所有异步操作执行完并且都成功的时候才回调Promise.
在使用的时候您可以将需要并行执行的Promise放到各个方法里,然后使用Promise.all([ ]) 在数组里写上需要调用的方法,即可并行执行各个Promise;

这看起来有点像axios.all()发起的并发请求? 确实,我也这么觉得…

function promise1(){
  let work1 = new Promise(function(resolve, reject){})
	return work1;
//return一下,不然Promise.all的数组里收到的不是Promise对象;
}
	   
function promise2(){
  let work2 = new Promise(function(resolve, reject){})
    return work2;
//return一下,不然Promise.all的数组里收到的不是Promise对象;
}
	   
function promise3(){
  let work3 = new Promise(function(resolve, reject){})
	return work3;
//return一下,不然Promise.all的数组里收到的不是Promise对象;
}

 //在此处同时调用三个方法,返回三个Promise;
	Promise.all([
	  promise1(),
	  promise2(),
	  promise3(),
	])
	.then((res) => {
	   console.log("拿到了!" + res);
		})
	.catch((error) => {
	   console.log("出错啦:" + error)
	  });

等到它们都执行完后,then()才会调用(catch()依然是出错就调用)。

3个异步操作返回的数据都在then里以数组的形式存储着, 这时候给then一个参数来接收结果即可.


总结

其实还该有个finally, 但是我还没接触过,怕写错了,这篇先放着,以后碰着再回来补上吧!

这是我根据本阶段学习得出的一些经验, 如果对您有帮助, 博主很荣幸.

以上是关于ES6的Promise详解的主要内容,如果未能解决你的问题,请参考以下文章

ES6之Promise用法详解

[转]JS - Promise使用详解2(ES6中的Promise)

ES6新增语法——Promise详解

ES6 Promise 详解

ES6 中 Promise 详解

Es6 Promise 用法详解