在js
中,有些任务并不是立即执行
的,如setTimeOut
或者发送网络请求
等等
这些任务或多或少的延时
,而这些延时
都可能会导致js代码实际的执行顺序和我们预计的不一样
有些时候我们也会需要这些任务完成后的返回值
来进行进一步的操作
如下面代码所示
function ajax(url) {
setTimeout(function () {
if (url == "http://yes") {
return true
} else {
return false
}
}, 1000)
}
var data = ajax("http://yes")
console.log(data)
这段代码中我们模拟了一个网络请求
,希望data
能接受到服务器返回的结果,可打印的结果却是undefined
为了保证代码的执行顺序,也为了保证我们能拿到需要的返回值,解决以上问题的方法就是使用回调函数
我们将以上的代码改写一下
function ajax(url, success, fail) {
setTimeout(function () {
if (url == "http://yes") {
success("请求成功")
} else {
fail("请求失败")
}
}, 1000)
}
ajax("http://yes", function (res) {
console.log(res)
}, function (err) {
console.log(err)
})
结果
我们会在1秒之后拿到结果
现在,我们的需求变成了需要监听页面中的一个按钮,当用户点击了这个按钮的1秒后发送网络请求
代码就变成了下面这样
function ajax(url, success, fail) {
setTimeout(function () {
if (url == "http://yes") {
success("请求成功")
} else {
fail("请求失败")
}
}, 1000)
}
document.querySelector("button").addEventListener("click", function () {
setTimeout(function () {
ajax("http://yes", function (res) {
console.log(res)
}, function (err) {
console.log(err)
})
}, 1000)
})
这种回调函数嵌套回调函数的做法被称之为回调地域
,为了能保证任务的执行顺序,以上代码都是通过硬编码
的形式解决
我们将setTimeOut
硬编码到click
中,再将ajax
硬编码到setTimeOut
中,以此类推
这种硬编码
所带来的无法复用
以及难以处理每个步骤中发生的异常情况
的问题是回调地狱
真正问题所在
在ES6
之后规定了一种新的编程范式Promise
,它可以很轻松的解决以上问题
通过new
创建Promise
对象时,我们需要传入一个回调函数
,这个回调函数
被称之为executor
这个回调函数
会立即执行,并且在传入2个回调函数
,resolve
和reject
当调用resolve
方法时会调用Promise
对象的then
方法传入的回调函数
当调用reject
方法时就会调用Promise
对象的catch
方法传入的回调函数
var promise = new Promise((resolve, reject) => {
resolve("success")
reject("fail")
})
promise.then((res) => {
console.log(res)
}).catch((err) => {
console.log(err)
})
在Promise
的使用过程中会出现三种状态
兑现状态
,位于这种状态时表示Promise
中的操作成功完成
,即调用了resolve
方法拒绝状态
,位于这种状态时表示Promise
中的操作失败
,即调用了reject
方法初始状态
,这种状态意味着此时Promise
既没有兑现也没有拒绝
,当Promise
没有调用resolve
和reject
方法时Promise
位于这种状态Promise
可以从pending
状态变成fulfilled
和rejected
状态Promise
无法从fulfilled/rejected
变为pending
和rejected/fulfilled
状态Promise
中同时调用了resolve
和reject
方法,那么只会执行最先调用的那个方法
不是无法执行
,而是状态被锁死无法改变
普通对象
或一个普通值
then
方法的回调函数的参数
,并且也会将这个Promise
的状态置为fulfilled
Promise
Promise
的状态将会决定原来Promise
的状态对象
,对象中有then方法
thenable
对象,执行这个then
方法,并根据then
方法的结果来决定Promise
的状态Promise
的实例方法
具体有以下三个
then
方法接收两个参数
,一个是成功的回调函数
,一个是失败的回调函数
var promise = new Promise((resolve, reject) => {
resolve("success")
reject("fail")
})
promise.then((res) => {
console.log(res)
}, (err) => {
console.log(err)
})
这种写法等价于上面演示的写法
一个Promise
的then
方法可以多次调用
var promise = new Promise((resolve, reject) => {
resolve("success")
})
promise.then((res) => {
console.log("res1", res)
})
promise.then((res) => {
console.log("res2", res)
})
then
方法也具有返回值
,他会返回一个Promise
因为返回的是一个Promise
所以能进行链式调用
而如果想要继续调用接下来的then
方法就需要在这个Promise
中调用resolve
方法
resolve
方法可以传入参数
,这里的参数
就是最开始的then
方法的回调函数的返回值
有以下三种情况
回调函数返回一个普通值
var promise = new Promise((resolve, reject) => {
resolve("success")
})
promise.then((res) => {
console.log(res)
return "aaa"
}).then((res) => {
console.log(res)
})
相当于then
方法返回一个Promise
,Promise
调用了resolve
方法,resolve
方法将aaa
作为参数传递
给了下一个then
方法
控制台结果如下
回调函数返回一个Promise
var promise = new Promise((resolve, reject) => {
resolve("success")
})
promise.then((res) => {
console.log(res)
return new Promise((resolve, reject) => {
resolve("aaa")
})
}).then((res) => {
console.log(res)
})
如果返回的是一个Promise
的会先等新的Promise
调用resolve
之后将resolve
的结果传递给下一个then
控制台结果同上
回调函数返回一个含有then方法的对象
var promise = new Promise((resolve, reject) => {
resolve("success")
})
promise.then((res) => {
console.log(res)
return {
then: function (resolve) {
resolve("aaa")
}
}
}).then((res) => {
console.log(res)
})
如果返回的是一个thenable
对象的话会等resolve
之后将对应的值
返回给下一个then
控制台结果同上
在运行then
中的回调函数
时Promise
的状态为pending
在回调函数返回一个结果
时Promise
的状态为fulfilled
在then
方法抛出一个异常
时Promise
的状态为rejected
catch
方法和then
方法一样可以多次调用
var promise = new Promise((resolve, reject) => {
reject("error")
})
promise.catch((res) => {
console.log("res1", res)
})
promise.catch((res) => {
console.log("res2", res)
})
catch
方法也会返回一个Promise
,所以我们也能在catch
后面链式调用then
或catch
值得注意的是,catch
的回调执行完后返回的Promise
默认状态为fulfilled
即会在catch
调用完后在接下来的链式调用中跳过catch
,调用then
如果想要调用接下来的catch
则需要抛出一个异常
var promise = new Promise((resolve, reject) => {
reject("error")
})
promise.catch((res) => {
console.log("res1", res)
throw new Error("error")
}).catch((res) => {
console.log("res2", res)
return "success"
}).catch((res) => {
console.log("res3", res)
return "error"
}).then((res) => {
console.log("res4", res)
})
finally
是在ES9
中新增的一个方法
表示无论Promise
对象无论变成fulfilled
还是rejected
状态,最终都会执行
的代码
finally方法不接收参数
var promise = new Promise((resolve, reject) => {
resolve("success")
})
promise.catch((res) => {
console.log("res1", res)
}).then((res) => {
console.log("res2", res)
}).finally(() => {
console.log("res3")
})
Promise
除了有实例方法
之外还有类方法
resolve
方法会将一个现成的内容转换成Promise来使用
var promise = Promise.resolve("success")
promise.then((res)=>{
console.log(res)
})
相当于
var promise = new Promise((resolve, reject) => {
resolve("success")
})
promise.then((res) => {
console.log(res)
})
和resolve
类似,只不过会调用reject方法
var promise = Promise.reject("error")
promise.catch((res) => {
console.log(res)
})
相当于
var promise = new Promise((resolve, reject) => {
reject("error")
})
promise.catch((res) => {
console.log(res)
})
控制台结果如下
all
方法会将多个Promise
包裹成一个新的Promise
,新的Promise
状态由包裹的所有Promise决定
当包裹的所有Promise
的状态都为fulfilled
时,新Promise
的状态为fulfilled
,并且会将包裹的所有Promise
的返回值组成一个数组
返回
如果包裹的Promise
中有一个状态为reject
时,新Promise
状态为reject
,并且会将第一个reject
的返回值
返回
var p1 = new Promise((resolve, reject) => {
resolve("p1 success")
})
var p2 = new Promise((resolve, reject) => {
resolve("p2 success")
})
var p3 = new Promise((resolve, reject) => {
reject("p3 error")
})
var p4 = Promise.all([p1, p2])
p4.then((res) => {
console.log(res)
})
var p5 = Promise.all([p2, p3])
p5.catch((res) => {
console.log(res)
})
allSettled
方法和all
方法类似,但不同的是allSettted
方法会等待所有Promise有结果后才会有最终的状态
,并且最终的状态一定为fulfilled
var p1 = new Promise((resolve, reject) => {
resolve("p1 success")
})
var p2 = new Promise((resolve, reject) => {
resolve("p2 success")
})
var p3 = new Promise((resolve, reject) => {
reject("p3 error")
})
var p4 = Promise.allSettled([p1, p2, p3])
p4.then((res) => {
console.log(res)
})
控制台结果如下
其中status
为Promise
的状态,value
为这个Promise
的返回值
race
同样将多个Promise
组合成一个新Promise
在这些Promise
中如果有一个结果
,新Promise
的状态就由这个有结果的Promise
决定
var p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p1 success")
}, 2000);
})
var p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p2 success")
}, 3000);
})
var p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("p3 error")
}, 100);
})
var p4 = Promise.race([p1, p2, p3])
p4.then((res) => {
console.log(res)
}).catch((res) => {
console.log(res)
})
控制台结果如下
和race
方法相似,不同的地方在于any
方法会一直等到第一个fulfilled状态
的Promise
出现再来决定新Promise
的状态
如果所有的Promise
均为reject
就会报一个AggregateError
的错误
var p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p1 success")
}, 2000);
})
var p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p2 success")
}, 3000);
})
var p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("p3 error")
}, 100);
})
var p4 = Promise.any([p1, p2, p3])
p4.then((res) => {
console.log(res)
})
var p5 = Promise.any([p3])
p5.catch((res) => {
console.log(res)
})