深入学习JavaScript的Promise和then、catch、finally的用法

一、“发布者-订阅者”模式

在讲JavaScript的Promise之前,得先讲讲“发布者-订阅者”模式。在“发布者-订阅者”模式中,发布者不会将消息直接发送给订阅者。发布者和订阅者不知道彼此的存在。在它们之间存在一个调度中心,调度中心负责将发布者发布的消息分发给各个订阅者。

深入学习JavaScript的Promise和then、catch、finally的用法_第1张图片

二、Promise介绍

Promise就是将“发布者代码”和“订阅者代码”连接在一起的一个特殊的JavaScript对象,也就是发布订阅模式中的调度中心。“发布者代码”可以花费任意长的时间来准备需要发布的消息,而Promise在发布消息完毕时,将消息向所有的“订阅者代码”公布。以下面的代码为例子,详细解析Promise的用法。

let myPromise = new Promise(function(resolve, reject){
    setTimeout(function(){ 
        resolve('公众号【愤怒的it男】发布新的文章')
    }, 3000);
})
myPromise.finally(function(){
    console.log("发布者发布消息:公众号【愤怒的it男】发布新的文章");
})
myPromise.then(
    function(value){console.log("订阅者A收到消息:"+value);},
    function(error){console.log("订阅者A未收到消息:"+error);}
)
myPromise.then(
    function(value){console.log("订阅者B收到消息:"+value);},
    function(error){console.log("订阅者B未收到消息:"+error);}
)
myPromise.then(
    function(value){console.log("订阅者C收到消息:"+value);},
    function(error){console.log("订阅者C未收到消息:"+error);}
)

输出结果为:

深入学习JavaScript的Promise和then、catch、finally的用法_第2张图片

三、发布者代码(Publisher)

使用new Promise注册发布者代码:

let myPromise = new Promise(function(resolve, reject){
    setTimeout(function(){ 
        resolve('公众号【愤怒的it男】发布新的文章')
    }, 3000);
})

在new一个Promise对象时,传递给Promise构造函数的参数即为发布者代码,它本身就是一个函数,并且会在new Promise对象时被立即调用执行:

function(resolve, reject){
    setTimeout(function(){ 
        resolve('公众号【愤怒的it男】发布新的文章')
    }, 3000);
}

它的参数resolve和reject是由JavaScript自身提供的回调。如果任务成功并带有结果value,则使用resolve(value)发布成功的结果value;如果任务失败并带有错误error(即error对象),则使用reject(error)发布失败的错误error。当然resolve和reject这两个参数名不是固定的,也可以使用其他的名称,只需知道第一参数为发布成功结果的回调,第二个参数为发布错误消息的回调。如上发布者代码则是在等待3秒超时后,使用resolve回调发布成功的结果(公众号【愤怒的it男】发布新的文章)

相关知识点1:

1、发布者代码同时只能调用一次resolve或一次reject,先调用的有效,后调用的都会被忽略。

2、发布者代码可以异步执行某些操作并在一段时间后调用resolve或reject,也可以立即调用resolve或reject。

3、resolve或reject只需要一个参数(或不包含任何参数),并且将忽略额外的参数。

相关知识点2:

由new Promise构造器返回的promise对象具有两个重要的内部属性:

1、state:初始值是“pending”,在调用resolve(value)后变为“fullfilled”,或者在调用reject(error)后变为“rejected”。state值变为“fullfilled”或“rejected”后,promise处于settled状态。

2、result:初始值是“undefined”,在调用resolve(value)后变为value,或者在调用reject(error)后变为error。

但promise对象的state和result属性都是内部的,我们无法直接访问它们。

深入学习JavaScript的Promise和then、catch、finally的用法_第3张图片

相关知识点3:

1、resolve()是用来表示promise的状态为fullfilled,相当于只是定义了一个有状态的Promise,但是并没有调用其他;

2、promise调用then的前提是promise的状态为fullfilled。

四、订阅者代码(Subscriber)

发布者代码通过调用resolve或reject发布成功的结果value或者失败的错误error,订阅者代码则通过promise的state和result接收成功的结果value或者失败的错误error。使用.finally/.then/.catch方法注册订阅者代码:

myPromise.finally(function(){
    console.log("发布者发布消息:公众号【愤怒的it男】发布新的文章");
})
myPromise.then(
    function(value){console.log("订阅者A收到消息:"+value);},
    function(error){console.log("订阅者A未收到消息:"+error);}
)
myPromise.then(
    function(value){console.log("订阅者B收到消息:"+value);},
    function(error){console.log("订阅者B未收到消息:"+error);}
)
myPromise.then(
    function(value){console.log("订阅者C收到消息:"+value);},
    function(error){console.log("订阅者C未收到消息:"+error);}
)

1、finally

myPromise.finally(function(){
    console.log("发布者发布消息:公众号【愤怒的it男】发布新的文章");
})

通过finally注册如上的订阅者代码,其中它的函数是没有参数的,因此在订阅者代码中是不知道 promise的state是“fullfilled”还是“rejected”,反正只要promise为settled状态时,都会执行订阅者代码,并且finally会将成功的结果value或者失败的错误error传递给下一个.finally/.then/.catch。所以finally注册的订阅者代码适合执行清理或其他善尾工作。

2、then

myPromise.then(
    function(value){console.log("订阅者A收到消息:"+value);},
    function(error){console.log("订阅者A未收到消息:"+error);}
)
myPromise.then(
    function(value){console.log("订阅者B收到消息:"+value);},
    function(error){console.log("订阅者B未收到消息:"+error);}
)
myPromise.then(
    function(value){console.log("订阅者C收到消息:"+value);},
    function(error){console.log("订阅者C未收到消息:"+error);}
)

如上代码则是通过then注册三个订阅者代码。

then的第一个参数是一个函数,该函数将在promise调用resolve发布成功的结果value后运行并接收value;then的第二个参数也是一个函数,该函数将在promise调用rejected发布失败的错误error后运行并接收error。

如果只对value感兴趣,可以只为then提供一个函数参数;如果只对error感兴趣,可以使用null作为then的第一个参数,如:then(null, errorHandlingFunction)。

3、catch

如上所说,如果只对error感兴趣,可以使用null作为then的第一个参数,如:then(null, errorHandlingFunction)。其实我们也可以使用catch(errorHandlingFunction),catch(errorHandlingFunction)是then(null, errorHandlingFunction)的简写形式。

五、Promise代码的另一种写法

Promise.resolve('公众号【愤怒的it男】发布新的文章')
// 等价于
new Promise(resolve => resolve('公众号【愤怒的it男】发布新的文章'))
Promise.reject('公众号【愤怒的it男】发布新的文章出错')
// 等价于
new Promise(reject => reject('公众号【愤怒的it男】发布新的文章出错'))

六、Promise的实际使用情形

1、等待超时示例代码:

let myPromise = new Promise(function(myResolve, myReject) {
    setTimeout(function() { myResolve("I love You !!"); }, 3000);
});
myPromise.then(function(value) {
    document.getElementById("demo").innerHTML = value;
});

2、等待请求示例代码:

let myPromise = new Promise(function(myResolve, myReject) {
    let req = new XMLHttpRequest();
    req.open('GET', "mycar.htm");
    req.onload = function() {
        if (req.status == 200) {
            myResolve(req.response);
        } else {
            myReject("File not Found");
        }
    };
    req.send();
});
myPromise.then(
    function(value) {myDisplayer(value);},
    function(error) {myDisplayer(error);}
);

 更多爬虫知识以及实例源码,可关注微信公众号【愤怒的it男】

你可能感兴趣的:(JavaScript,学习,javascript,开发语言,前端,爬虫)