Promise的基本使用
Promise的基本使用
Promise是ES6推出的新特性之一,是异步编程的一种解决方案,比传统的回调函数和时间更加合理和强大。
0、回调函数
要搞明白Promise之前,我们必须要知道“回调函数”的概念。那什么是回调函数呢?
你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。回答完毕。引用知乎 原作者:常溪玲
接下来我们通过一个案例来认识回调函数
一个回调函数的案例:
created () {
this.shopping(this.callbackMethodInstance, '10086', 'iPhone13')
},
methods: {
callbackMethodInstance (phoneNumber, goodsName) {
console.log(
`号码为${phoneNumber}的用户您好,您想购买的${goodsName}到货了可以来店里取货啦`
)
},
shopping (callbackMethod, phoneNumber, goodsName) {
console.log(`${goodsName}暂时缺货,我们已经登记了号码,货到了会给您回电`)
setTimeout(() => {
callbackMethod(phoneNumber, goodsName)
}, 2000)
}
}
}
控制台打印:
iPhone13暂时缺货,我们已经登记了号码,货到了会给您回电
// 2秒后打印
号码为10086的用户您好,您想购买的iPhone13到货了可以来店里取货啦
从上面这个案例可以看出,所谓回调函数不过是将函数作为方法传递,在一个函数内部的指定位置来执行这个函数而已。
1、Promise Quickstart
export default {
created () {
this.firstPromise('10086', 'iPhone13').then(data => {
console.log(
`号码为${data.phoneNumber}的用户您好,您想购买的${data.goodsName}到货了`
)
})
},
methods: {
firstPromise (phoneNumber, goodsName) {
let p = new Promise(resolve => {
console.log(
`${goodsName}暂时缺货,我们已经登记了号码,货到了会给您回电`
)
setTimeout(() => {
resolve({ phoneNumber, goodsName })
}, 2000)
})
return p
}
}
}
控制台打印:
iPhone13暂时缺货,我们已经登记了号码,货到了会给您回电
// 2秒后打印
号码为10086的用户您好,您想购买的iPhone13到货了可以来店里取货啦
从上面案例的结果可以看出,resolve起到了一个回调函数的作用,可以在异步操作执行结束之后返回一个结果。可以通过.then的形式来调用这个回调函数。它和回调函数的区别就是,没有将函数作为参数,使用resolve函数代替,resolve中只能有一个参数,多余的参数会被忽略。使用.then的形式来调用这个“回调函数”
.catch的用法
看到这里,如果你用过axios的话你会发现,这不就是axios中的.then.catch么?没错,axios使用了Promise进行了封装,.then我们知道是怎么回事了,那么.catch怎么用呢?
我们知道catch一般是用来捕获异常的,在Promise中通常也这么用。在Promise中管理了一个“状态”,我们前面看到的resolve是将状态设置为fullfiled,我们可以粗浅的理解它为“成功状态”。那与之对应的一定有“失败状态”对吧,reject函数是将Promise的状态设置为rejected,我们可以简单的把它理解为“失败状态”。注:这里所说的成功和失败状态不是严谨的说法,只是为了方便我们理解而已。
继续来看案例:
export default {
created () {
this.firstPromise('10086', 'iPhone13')
.then(data => {
console.log(
`号码为${data.phoneNumber}的用户您好,您想购买的${data.goodsName}到货了`
)
})
.catch(error => {
console.log(
`号码为${error.phoneNumber}的用户抱歉,您想购买的${error.goodsName}已经过了两个月了,仍然没有货`
)
})
},
methods: {
firstPromise (phoneNumber, goodsName) {
let p = new Promise((resolve, reject) => {
console.log(
`${goodsName}暂时缺货,我们已经登记了号码,货到了会给您回电`
)
setTimeout(() => {
if (goodsName === 'iPhone12') {
resolve({ phoneNumber, goodsName })
} else {
reject({ phoneNumber, goodsName })
}
}, 2000)
})
return p
}
}
}
控制台打印:
iPhone13暂时缺货,我们已经登记了号码,货到了会给您回电
// 2秒后打印
号码为10086的用户抱歉,您想购买的iPhone13已经过了两个月了,仍然没有货
这一次,很明显.then没有执行,.catch执行了。这是因为firstPromise中执行了reject,状态修改为了rejected。此时只有.catch会被执行。
Promise的三种状态
- pending-进行中
- fulfilled-成功
- rejected-失败
Promise对象,可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数,但Promise对象的状态不受外界影响,Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败),只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态,这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
状态的改变(或者说决议)不可逆,一旦决议就不能再更改。
任何时候都可以得到这个结果,Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected,只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型),如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果,这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
我们再来看一个案例:
export default {
created () {
// this.shopping(this.callbackMethodInstance, '10086', 'iPhone13')
this.firstPromise('10086', 'iPhone13')
.then(data => {
console.log(
`号码为${data.phoneNumber}的用户您好,您想购买的${data.goodsName}到货了`
)
})
.catch(error => {
console.log(
`号码为${error.phoneNumber}的用户抱歉,您想购买的${error.goodsName}已经过了两个月了,仍然没有货`
)
})
},
methods: {
firstPromise (phoneNumber, goodsName) {
let p = new Promise((resolve, reject) => {
console.log(
`${goodsName}暂时缺货,我们已经登记了号码,货到了会给您回电`
)
setTimeout(() => {
resolve({ phoneNumber, goodsName })
}, 2000)
reject({ phoneNumber, goodsName })
})
return p
}
}
}
控制台打印:
iPhone13暂时缺货,我们已经登记了号码,货到了会给您回电
// 立即打印
号码为10086的用户抱歉,您想购买的iPhone13已经过了两个月了,仍然没有货
这个案例不是很好,但是我们可以看出在执行了reject函数后,没有继续的等待resolve执行。这就是我们前面说的Promise状态的改变(或者说决议)不可逆,一旦决议就不能再更改。
2、回调地狱
如果只是上面这点作用,那Promise也不过如此嘛,不就是回调函数换了一种写法而已。那如果回调函数中又有一个异步操作需要回调函数,它内部还有异步操作呢?这就是我们所说的“回调地狱”。
回调地狱案例代码:
function sayNum(name, callback) {
setTimeout(() => {
console.log(name);
callback()
}, 1000)
}
sayNum('one', function () {
sayNum('two', function () {
sayNum('three', function () {
sayNum('four', function () {
console.log('结束了');
})
})
})
})
//打印结果 one two three four 结束了
Promise链式操作
Promise是避免回调地狱的一个进步,通过.then( )的链式操作来触发多个异步任务,并且保证执行顺序,使得代码更易于阅读和维护。
怎么样,经过Promise化后,代码是不是看起来清晰多了。
function fn(num) {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log(num);
resolve(num)
}, 1000)
})
}
var p1 = fn('one')
p1.then((data) => {
return fn('two')
})
.then((data) => {
return fn('three')
})
.then((data) => {
return fn('four')
})
// 打印结果 one two three four
3、Promise补充-两个对象方法
all的用法
Promise.all( ): 并发处理多个异步任务,所有任务都执行完成才能得到结果,并发处理的任务都是按照顺序执行的
var p1 = fn('第一次')
var p2 = fn('第二次')
var p3 = fn('第三次')
Promise.all([p1, p2, p3]).then((res) => {
console.log(res);
})
// 打印 ['第一次','第二次','第三次']
race的用法
Promise.race( ):并发处理多个异步任务,只要有一个任务完成就能得到结果
var p1 = fn('第一次')
var p2 = fn('第二次')
var p3 = fn('第三次')
Promise.all([p1, p2, p3]).then((res) => {
console.log(res);
})
// 打印 第一次
参考: