写这篇文章是为了更深入的了解promise,有不对的地方还希望小伙伴们能指出。
什么是promise?
从字面上理解,promise是承诺的意思,它承诺将来会给你一个答复。它是JavaScript中执行异步任务的新解决方案。(旧的是纯回调式的解决方案)promise是一个构造函数,它用来封装一个异步操作,并获取异步操作的结果。
promise能做什么?
1、指定回调函数的方式更加灵活,异步的回调如果提前不指定,一旦异步任务完成,就很容易错过需要获取的数据。promise可以在异步任务启动后指定一个时间去回调函数并获取数据。 2、支持链式调用,可以解决地狱回调问题(解决地狱回调的方案还有async/await)
promise的三种状态
promise内部有一个很重要的东西,就是状态。
1、 pending,等待状态,如正在进行网络请求,或者定时器没有到时间。
2、fulfill,满足状态,当我们主动回调resolved时,就处于满足状态,并且会回调.then()方法。
3、reject,拒绝状态,当我们主动回调reject时,就处于拒绝状态,并且会回调.catch()方法。
promise的状态改变
promise的状态改变只有两种,并且只能改变一次
1、pending—>resolved
2、pending—>rejected
无论状态的改变是成功还是失败,都会有一个结果,成功的结果是value,失败的结果是reason。
promise的执行流程
// 1、创建一个新的promise
let test = new Promise((resolve,reject) => {
// 2、异步操作
setTimeout(() => {
let time = Date.now() // 当前时间
// 3、状态变更
if(time%2 === 0) { // 如果当前时间是偶数,就调用resolve(value)
resolve('当前时间是偶数'+time)
} else { // 如果当前时间是奇数,就调用reject(season)
reject('当前时间是奇数')
}
},1000)
})
test.then(
value => { // 接收成功的数据onResolved()
console.log('成功',value)
},
reason => { // 接收失败的数据onRejected()
console.log('失败',reason)
}
)
手写一个promise
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
// reject('失败')
},1000)
}).then(
value => {
console.log(value)
}
).catch(
reason => {
console.log(reason)
}
)
解决地狱回调问题
什么是地狱回调?在弄懂地狱回调之前,我们首先需要明白两个概念:
1、什么是回调函数?
当一个函数(函数1)作为参数传递给另外一个函数(函数2)时,由另外一个函数(函数2)执行时来调用这个函数(函数1),这就是回调函数。主函数先执行,执行完成后将该函数作为参数传给下一个函数,
2、什么是异步任务?
当一个任务不进入主线程,被放到异步任务队列里,前一个任务的执行是否结束,不影响下一个任务的执行。这就是异步任务。 回调函数有两种类型
2.1同步调用回调函数
会立即执行,全部执行了以后才结束,不会放入回调队列中。
let array = [1,2,3,4,5]
array.forEach(item => {
console.log(item)
})
console.log('执行顺序')
2.2异步调用回调函数
不会立即执行,会放入回调队列中将来执行。
setTimeout(() => {
console.log('定时器内部')
},0)
console.log('定时器外部')
3、解决异步调用问题
// 成功的回调函数
function successCallback(result) {
console.log('成功了' + result)
}
// 失败的回调函数
function failureCallback(error) {
console.log('失败了' + error)
}
先用纯回调的方法来解决一下这个问题
// 使用纯回调函数解决异步调用
test(toDo, successCallback, failureCallback)
用promise解决
// 使用promise
let promise = test(toDo)
setTimeout(() => {
promise.then(successCallback, failureCallback)
},1000)
弄懂这些之后,我们来接着捋回调地狱,什么是回调地狱呢?写一段示例代码
// 地狱回调伪代码
toDo1(function(result1) {
toDo2(result1, function(result2) {
toDo3(result2, function(result3) {
console.log(result3)
})
})
})
上段代码中,回调函数内部嵌套了回调函数,这种情况就叫做地狱回调。 现在,我们在上面学习的promise就可以出场来解决这个问题了。
// 使用promise的链式调用解决回调地狱
toDo1()
.then(function(result1) {
return toDo2(result1)
})
.then(function(result2) {
return toDo3(result2)
})
.then(function(result3) {
console.log(result3)
})
.catch(failureCallback) // 失败的回调
当然promise不是地狱回调的唯一解决方法,还有一种解决方法是使用async/await来解决。
// 使用async/await解决回调地狱
async function request() {
try {
let result1 = await toDo1()
let result2 = await toDo2(result1)
let result3 = await toDo3(result2)
console.log(result3)
} catch(error) {
failureCallback(error)
}
}
拓展知识
实例对象与函数对象
1、实例对象:
用new关键字创建出来的对象称为实例对象,简称对象。
let function = new Function() // Function是构造函数,function 是实例对象
2、函数对象:
当我们将一个函数当做对象使用时,就称为函数对象。函数本身就是一个对象。
let function = new Function()
console.log(Function.prototype) // Function是函数对象
Function.call({}) // 这行代码表示调用Function函数对象的call方法。
注:只有函数对象才有bind、call、apply等方法,实例对象上面没有这些方法。 小技巧:括号的左边是函数,点的左边是对象
ERROR的理解与处理
1、错误的类型
Error:所有错误的父类型 ReferenceError:引用错误,引用的变量不存在
console.log(a)
TypeError:类型错误,数据类型不正确
let a
console.log(a.xxx)
RangeError:范围错误,数据的值不在允许范围内
function demo() {
demo()
}
demo()
SyntaxError:语法错误
let abc = " "" "
2、错误的处理
捕获错误:try…catch
try {
let abc
console.log(abc.a)
} catch(error) { // error对象里面有两个属性,message和stack
console.log(error)
console.log(error.message)
console.log(error.stack)
}
console.log('错误已被捕获')
抛出错误:throw error
function demo() {
if(Date.now()%2===1) {
console.log("当前时间为奇数")
} else {
throw new Error('当前时间为偶数')
}
}
try{
demo()
} catch(error) {
alert(error.message)
}
抛出错误
3、错误对象
error是一个对象,它有两个属性
1、message属性:错误相关信息
2、stack属性:函数调用栈记录信息