ES6 - Promise对象①:概述、then()、catch()

 1.含义

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

(1)Promise对象的两个特点

① 对象的状态不受外界影响

Promise对象代表一个异步操作,有三种状态:pending(进行中)fulfilled(已成功)rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

②一旦状态改变,就不会再变,任何时候都可以得到这个结果

Promise对象的状态改变,只有两种可能:从pending -> fulfilled和从pending -> rejected。只要这两种情况发生,状态就凝固了,不会再发生改变。

后面的resolved统一只指fulfilled状态,不包含rejected状态。

(2)缺点

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

2.基本用法

(1)创造一个Promise实例

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

 1 const promise = new Promise(function(resolve,reject){
 2 
 3     // 其他代码
 4 
 5     if(/* 异步操作成功 */){
 6         resolve(value)
 7     }else{
 8         reject(err)
 9     }
10 })

Promise构造函数接受一个函数作为参数,该函数有两个参数:resolverejected,它们是两个函数,由JavaScript引擎提供,不用自己部署:

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

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数

promise.then(function(value){
  // 成功的回调
},function(err){
  // 失败的回调
});

它们都接受Promise对象传出的值作为参数.

示例:

 1     function timeout(ms) {
 2         return new Promise((resolve, reject) => {
 3             setTimeout(() => {
 4                 resolve("异步执行结束")
 5             }, ms)
 6         })
 7     }
 8 
 9     timeout(1000)
10         .then((value) => { console.log(value) })  // 1s 后输出:异步执行结束

上面代码中,timeout方法返回一个Promise实例,说明过一段时间才会发生结果。过了指定的时间(ms参数)以后,Promise实例的状态变为resolved,就会触发then方法绑定的回调函数。

注意:Promise新建以后就会立即执行。

 1     let promise = new Promise(function (resolve, reject) {
 2         console.log("Promise");
 3         resolve()
 4     })
 5 
 6     promise.then(function () {
 7         console.log("异步结束");
 8     })
 9 
10     console.log("hello world");
11 
12     // Promise
13     // hello world
14     // resolved

上面代码中,Promise新建以后立即执行,所以首先输出Promise。然后.then方法的回调函数,将在当前脚本所在同步任务执行完才会执行,所以异步结束最后输出。这里涉及到JS的事件循环,详细可见

注意:

  • 即使没有调用.then()方法,promise内的log方法也会执行:
1     let promise = new Promise(function (resolve, reject) {
2         console.log("Promise");
3         resolve()
4     })  // Promise
  •  调用resolve或rejected并不会终结Promise的参数函数的执行。
1     let promise = new Promise(function (resolve, reject) {
2         console.log("Promise");
3         resolve()
4         console.log("Promise仍在执行");
5     }) 
6      // Promise
7      // Promise仍在执行
  • 一般来说,resolve以后,Promise的使命便结束了,后续操作应该放到.then方法里面,而不应该直接写在resolve或reject后面,所以最好加上return语句:
    • new Promise((resolve, reject) => {
        return resolve(1);
        // 后面的语句不会执行
        console.log(2);
      })

 3.Promise.prototype.then()

 then方法是定义在原型对象Promise.prototype上的。它的作用是为Promise实例添加状态改变时的回调函数。

 then方法的两个参数分别是resolved状态的回调函数和rejected状态的回调函数,它们都是可选的

then方法返回的是一个新的Promise实例(不是原来的哪个Promise实例),因此可以使用链式写法(then后面接另一个then)

getJSON("/json").then(function(json){
   return "json"
}).then(function(){
  // ...
})

示例:

 1     let promise = new Promise(function (resolve, reject) {
 2         console.log("Promise");
 3         return resolve()
 4     }) 
 5 
 6     promise.then(function () {
 7         console.log("Promise结束");
 8         return "异步结束"
 9     }).then((res)=>{
10         console.log(res);
11     })
12 
13     // Promise
14     // Promise结束
15     // 异步结束

说明:第一个回调函数完成以后,会将返回结果(“异步结束”)作为参数传入第二个回调函数

  采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。

4.Promise.prototype.catch()

 Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

getJSON('/posts.json').then(function(posts) {
  // ...
}).catch(function(error) {
  // 处理 getJSON 和 前一个回调函数运行时发生的错误
  console.log('发生错误!', error);
});

上面说过Promise的状态从pending -> rejected状态时会将异步操作的错误作为参数传递出去:

示例1:

 1     let promise = new Promise(function (resolve, reject) {
 2         console.log("Promise");
 3         return reject("发生不明错误")
 4     })
 5 
 6     promise.then(function () {
 7     }).catch(err => {
 8         console.log(err);
 9     })
10 
11     // Promise
12     // 发生不明错误

示例2:

1     const promise = new Promise(function (resolve, reject) {
2         throw new Error('test');
3     });
4     promise.catch(function (error) {
5         console.log(error);
6     });
7     // Error:test

其实reject()方法的作用,等同于抛出错误。

注意:如果Promise状态已经变成了resolved,再抛出错误是无效的

Promise对象的错误具有“冒泡新值”,会一直向后传递,直到被捕获位置。也就是说,错误总是会被下一个catch语句捕获。

 1 let promise = new Promise(function(resolve,reject){
 2     reject(123)
 3 })
 4 
 5 promise.then()
 6     .then()
 7     .then()
 8     .catch(err =>{
 9         console.log(err);
10     })
11     // 123

 

建议:使用catch()方法,而不是使用then()方法的第二个参数来不湖泊then方法执行中的错误。

  因外第二种写法可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)

跟传统的try/catch代码块不同的是,如果没有使用catch()方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应。

 1 const someAsyncThing = function() {
 2   return new Promise(function(resolve, reject) {
 3     // 下面一行会报错,因为x没有声明
 4     resolve(x + 2);
 5   });
 6 };
 7 
 8 someAsyncThing().then(function() {
 9   console.log('everything is great');
10 });
11 
12 setTimeout(() => { console.log(123) }, 2000);

上面代码中,someAsyncThing()函数产生的Promise对象,内部有语法错误。浏览器运行到这一行,会打印出错误提示ReferenceError: x is not defined。但是不会退出进程,终止脚本运行,2s后还是会输出123。说明Promise内部的错误不会影响到Promise外部的代码,通俗说法就是"Promise 会吃掉错误"。

一般总是建议,Promise 对象后面要跟catch()方法,这样可以处理 Promise 内部发生的错误。catch()方法返回的还是一个 Promise 对象,因此后面还可以接着调用then()方法。如上面的示例:

 1     const someAsyncThing = function () {
 2         return new Promise(function (resolve, reject) {
 3             // 下面一行会报错,因为x没有声明
 4             resolve(x + 2);
 5         });
 6     };
 7 
 8     someAsyncThing()
 9         .catch(function (error) {
10             console.log('oh no', error);
11         })
12         .then(function () {
13             console.log('carry on');
14         });
15     // oh no [ReferenceError: x is not defined]
16     // carry on

 

上面代码运行完catch方法指定的回调函数,会接着运行后面那个then()方法指定的回调函数。如果没有报错,则会跳过catch()方法

Promise.resolve()
.catch(function(error) {
  console.log('oh no', error);
})
.then(function() {
  console.log('carry on');
});
// carry on

上面代码中,carch()方法抛出了一个错误,因为后面没有别的catch()方法了,所以这个then中的错误不会被捕获,也不会传递到外层。如果改写以下,结果就不一样了。

 1     const someAsyncThing = function () {
 2         return new Promise(function (resolve, reject) {
 3             // 下面一行会报错,因为x没有声明
 4             resolve(x + 2);
 5         });
 6     };
 7 
 8     someAsyncThing().then(()=>{
 9         return "hello"
10     }).catch((err)=>{
11         console.log("张三");
12         y + 2
13     }).catch(err=>{
14         console.log("捕获错误:",err);
15     })
16     // 张三
17     // 捕获错误: ReferenceError: y is not defined

这里用的是第二个catch()用来捕获第一个catch()方法抛出的错误

 

 

 

 

 

 

posted @ 2021-01-31 22:04  俄罗斯方块  阅读(331)  评论(0编辑  收藏  举报