概念
Promise对象用于一个异步操作的最终完成(或失败)及其结果值的表示。简单来说是用来处理异步请求。
window.Promise是JS的一个内置对象。回调时代
在以前处理异步的代码时,都是用的回调函数。以下面代码为例:
假如当点击一个按钮时,发起一个AJAX请求,click函数接受两个回调函数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17button.onclick = function(){
click('GET','/xxx.js',function(xxx){
console.log(xxx)
},function(yyy){
alert(yyy)
})
}
function click(method,url,successCallback,errorCallback){
$.ajax({
type: method,
url: url,
success: function(responseText){
successCallback(responseText)
},
error: function(xhr){
errorCallback('请求错误,错误码为'+xhr.status)
})这是一个典型的回调函数,不难发现在调用click函数,你得传四个参数,假如过几天你忘记了
click函数里的代码,等你要调用这个函数时,你得看代码我应该传哪些参数,这些参数的顺序是什么?显然这样很不方便,接下来就要引入我们的主角Promise。新时代,Promise来了
Promise的思路是,
click函数返回一个Promise对象,在这个返回对象上,你可以调用Promise原型的方法then,在then上挂两个回调函数,Promise给出了规定,若请求成功则执行then上的第一个回调,若请求失败则执行第二个回调。1
2
3
4
5
6
7
8
9
10button.onclick = function(){
let promise = $.ajax({type: 'GET',url: '/xxx.js',dataType: 'html'})
promise.then(success,error)
function success(responseText){
console.log(responseText)
}
function error(xhr){
alert('请求失败,错误码为'+ xhr.status)
}
}为了我们的代码更加具有可读性和可维护性,我们需要将数据请求和数据处理明确的区分开来。同时你都不需要给你函数
起名字。在出现Promise之后, 用Promise写异步代码已成为一种趋势。1
.then(success,error)
此外,用
then还有一个优点,它可以连续then多次,这是用回调无法比拟的。1
promise.then(success,error).then(success1,error1)
或
1
2promise.then(success,error)
promise.then(success1,error1)虽然请求成功之后,都会依次执行success和success1。这两段代码还是有点区别的。因为
then会返回一个新的Promise对象。前段代码的第二个then的回调函数传的参数是第一个then的回调函数的返回值。而后段代码的两个then都是在同一个Promise对象上。Promise的几种状态
Promise接口的基本思想是,异步任务返回一个Promise对象。
Promise对象只有三种状态。1
2
3异步操作"未完成"(pending)
异步操作"已完成"(fulfilled,又称resolved)
异步操作"失败"(rejected)这三种的状态的变化途径只有两种。

这种变化只能发生一次,如果是fulfilled状态,则Promise不能再转换成其它状态了,且必须会接受一个值。如果是rejected状态,则Promise也不能转换成其他状态,且必须抛出一个错误。Promise属性
Promise自身的属性和方法
1. Promise.length 长度属性,值为1,表示构造器参数的数目。
2. Promise.prototype Promise的原型
3. Promise.all()
这个方法可以接受一个可迭代的对象作为参数,例如数组。Promise.all()表示所传的对象里所有的Promise对象变为
fulfilled状态时,或者有一个Promise对象变为rejected时,便会返回一个Promise对象,这个对象才会去调用then方法,根据改变后的状态来判断执行then传的哪一个参数。
该方法的应对场景,当一个AJAX请求,它的参数需要2个或者更多请求都有返回结果之后才能确定。4. Promise.race()
该方法接受一个对象,当这个对象里的任何一个Promise对象状态变为
fulfilled或rejected时,并把该子Promise对象的成功返回值或拒绝详情作为参数传给父Promise绑定的句柄,同时返回一个Promise对象。Promise原型的属性和方法
1.
then方法- 一个Promise必须提供一个
then方法。 then一定返回的是Promise对象。- 对于一个Promise,
then可以调用多次。 then只接受两个为函数的参数。这两个参数是可选的。如果参数不是函数,就忽略它。这两个参数都只能调一次。第一个参数函数接收fulfilled状态的执行,第二个参数接收rejected状态的执行。
fn().then(function(){}),不管fn函数里接受参数的代码时同步还是异步的,都要保证then是异步的。then的链式调用1
2
3
4
5
6p.then(step1)
.then(step2)
.then(
console.log,
console.error
)
当p的状态变为
fulfilled时,就依次调用后面每一个then指定的回调函数,每一步都等到前一步执行完成,才会执行。console.log只会显示step2的返回值,console.error只会显示step1和step2中任意一个发生的错误。若step1操作失败,则step2不会执行,而是把由step1抛出的错误传递给console.error打出。可见Promise的错误具有传递性。2.
catch方法Promise.catch(onRejected)返回一个Promise对象,只处理拒绝的情况。它的行为与Promise.then(undefined,onRejected)相同。
Promise的执行顺序
我们知道Promise是异步的,而定时器接口也是异步的。那么它们哪个先执行?
1
2
3
4
5
6
7
8
9
10
11
12
13button.onclick = function(){
setTimeout(function(){console.log(1),0})
console.log(2)
x().then(success)
console.log(3)
function x(){
return new Promise(function(resolve,reject){
console.log(4)
resolve()
})
}
function success(responseText){console.log(5)}
}控制台打印的结果为
1
2
3
4
52
4
3
5
1我们可以推理出,x()是同步执行的,后面的
then(success)是异步的。定时器也是异步的,虽然then和setTimeout都是属于‘下一批’执行的。但Promise比setTimeout稍微快一点。
Promise的“下一批”属于microTask,定时器的”下一批”属于macroTask。这两个“下一批”并不是”同一批”。参考文章
- 一个Promise必须提供一个