在讲JavaScript的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);}
)
输出结果为:
使用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、发布者代码同时只能调用一次resolve或一次reject,先调用的有效,后调用的都会被忽略。
2、发布者代码可以异步执行某些操作并在一段时间后调用resolve或reject,也可以立即调用resolve或reject。
3、resolve或reject只需要一个参数(或不包含任何参数),并且将忽略额外的参数。
由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属性都是内部的,我们无法直接访问它们。
1、resolve()是用来表示promise的状态为fullfilled,相当于只是定义了一个有状态的Promise,但是并没有调用其他;
2、promise调用then的前提是promise的状态为fullfilled。
发布者代码通过调用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);}
)
myPromise.finally(function(){
console.log("发布者发布消息:公众号【愤怒的it男】发布新的文章");
})
通过finally注册如上的订阅者代码,其中它的函数是没有参数的,因此在订阅者代码中是不知道 promise的state是“fullfilled”还是“rejected”,反正只要promise为settled状态时,都会执行订阅者代码,并且finally会将成功的结果value或者失败的错误error传递给下一个.finally/.then/.catch。所以finally注册的订阅者代码适合执行清理或其他善尾工作。
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)。
如上所说,如果只对error感兴趣,可以使用null作为then的第一个参数,如:then(null, errorHandlingFunction)。其实我们也可以使用catch(errorHandlingFunction),catch(errorHandlingFunction)是then(null, errorHandlingFunction)的简写形式。
Promise.resolve('公众号【愤怒的it男】发布新的文章')
// 等价于
new Promise(resolve => resolve('公众号【愤怒的it男】发布新的文章'))
Promise.reject('公众号【愤怒的it男】发布新的文章出错')
// 等价于
new Promise(reject => reject('公众号【愤怒的it男】发布新的文章出错'))
let myPromise = new Promise(function(myResolve, myReject) {
setTimeout(function() { myResolve("I love You !!"); }, 3000);
});
myPromise.then(function(value) {
document.getElementById("demo").innerHTML = value;
});
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男】