javascript异步原理详解
2020年5月29日大约 6 分钟约 1755 字
什么是回调
回调是将函数作为参数传递给另一个函数
回调主要用于异步函数中,其中一个函数必须等待另一个函数执行完,比如等待网络数据加载完
const hello = function (callback) {
// 该函数执行逻辑
console.log('hello')
// 该函数执行完毕,执行回调函数
callback()
}
const worldCallback = function() {
console.log('world')
}
// 执行hello函数
hello(worldCallback)
什么是异步
异步代码会立即返回
const helloWorld = function() { console.log('Hello World') }
// 等待2秒后执行helloWorld函数
setTimeout(helloWorld, 2000) // 因为是异步,这里会立即返回执行后面代码
// 每隔5秒执行一次
setInterval(helloWorld, 5000) // 立即返回
clearTimeout(timeoutVariable)
方法可以中途停止TimeoutclearInterval(intervalVariable)
方法可以中途停止Interval
Promise对象
回调的另一种写法 ———— Promise
基本用法
Promise对象链接了生产代码和消费代码
// Promise创建后立即同步执行里面的内容,请注意这里是同步执行,生产代码本身是同步执行
const myPromise = new Promise(function(resolve, reject) {
// 执行生产代码...
console.log('正在执行生产代码')
const isSuccess = true // 生产代码是否执行成功
// 生产代码执行完毕
// 开始回调
// 修改状态准备执行消费代码
if( isSuccess === true ) {
// 如果代码执行成功,修改状态为 被解决
resolve('我已经解决了承诺,执行成功') // 注意这里是异步,会立即返回但不会马上执行消费代码
} else {
// 如果代码执行失败,修改状态为 被拒绝
reject('我拒绝这个承诺,执行失败') // 注意这里是异步,会立即返回但不会马上执行消费代码
}
})
console.log('Promise生产代码执行完毕')
// 注册消费代码,等待Promise改变状态后会执行的代码。注意这里还会返回一个Promise对象
myPromise.then(
function(value) { console.log(value) /* resolve被解决状态的函数代码...(成功时执行) */ }, // 可选
function(value) { console.log(value) /* reject被拒绝状态的回调函数代码...(失败时执行) */ } // 可选
)
// 注册reject状态的回调函数,上面写过了这里可以不写,分开只是为了更好的语义化,如果写了两个回调函数都会执行,上面的reject状态回调函数会先执行。注意这里会返回Promise对象
myPromise.catch(function(error){
console.log('生产代码执行中出现异常')
})
- 当Promise变成reject状态或者生产代码异常错误,reject状态回调函数都会被执行
- 方法
then
catch
会返回一个新的Promise,默认是resolved状态
Promise的错误不会消失,直到被捕获为止
// 常规使用
const myPromise = new Promise(function(resolve, reject){
reject('错误')
})
const newPromise = myPromise.then(function(value){ console.log(value) })
newPromise.catch(function(value){console.log(value)})
// 简洁使用,并且使用箭头函数代替
new Promise((resolve, reject)=>{
reject('错误')
}).then(value=>{
console.log(value)
}).catch(value=>{
console.log(value)
})
处理异常的一些方法:
new Promise((resolve, reject)=>{
reject('异常1')
}).then(value=>{
console.log(value)
}).catch(value=>{
throw new Error('异常2') //捕获到异常直接抛出
}).catch(value=>{
console.log(value) // 输出 异常2
// 手动返回新的Promise,替换掉默认
return new Promise((resolve, reject)=>{
reject('异常3')
})
}).catch(value=>{
console.log(value) // 输出 异常3
})
使用提供的快捷方式创建Promise对象
const myPromise1 = Promise.resolve('我已经解决了承诺,执行成功')
myPromise1.then(function(value){ console.log(value)})
myPromise1.then(function(value){ console.log('第二个处理函数'+value)}) // 其实可以有多个处理函数
myPromise1.catch(function(value){ console.log(value)})
const myPromise2 = Promise.reject('我拒绝这个承诺,执行失败')
myPromise1.then(function(value){ console.log(value)})
myPromise1.catch(function(value){ console.log(value)})
异步+回调
// Promise创建后立即执行里面的内容
new Promise((resolve, reject) => {
console.log('开始执行异步代码...')
setTimeout(()=>{ // 这里是异步,会立即返回执行后续代码
for(const t=Date.now(); Date.now()-t<=3000;){} // 同步阻塞3秒
resolve('异步代码执行完毕.')
}, 1000)
}).then((value)=>{
console.log('回调函数:' + value)
})
console.log('执行项目其他代码...')
for(const t=Date.now(); Date.now()-t<=500;){} // 同步阻塞0.5秒
console.log('其他代码执行完毕.')
async语法
async
和await
可以让我们用一种更简洁的方式写出基于Promise的异步行为,而无需刻意的链式调用Promise。
函数前加关键字async
,使函数返回一个Promise对象:
- 如果在
async
函数中return
一个数据,async
会把数据通过Promise.resolve()
封装成Promise对象 - 如果
async
函数没有返回值,它将返回Promise.resolve(undefined)
简单的说,async
是一定会返回一个Promise对象,不管内部返回的是不是Promise对象,都会被包装成Promise对象。
以上代码让我们用async重写:
// 这里的Promise的内容没有立即调用
const myPromise = async function() {
// 执行生产代码...
console.log('正在执行生产代码')
const isSuccess = true // 生产代码是否执行成功
// 生产代码执行完毕
// 开始回调
// 执行消费代码
if(isSuccess === true) {
return '执行成功' // 这是其实像resolve('执行成功')
}
}
// 手动调用Promise对象
myPromise().then(
function(value) { /* resolve回调函数代码...(成功时执行) */ }, // 可选
function(value) { /* reject回调函数代码...(失败时执行) */ } // 可选
)
async+await语法
await
会阻塞代码等待语句操作完成
await
在等待Promise的resolved状态传的值- 如果
await
等到的不是一个Promise对象,那跟着表达式的运算结果就是它要等到的东西 await
仅能在async
内使用,而且通常await后面会跟一个异步的Promise
可以这么理解:
async
函数内在遇到await后会立即返回Promise对象,继续执行async函数的外部逻辑- 准确的说
async
函数内在await后面的Promise对象被生成完成后,async
会立刻返回一个Promise对象(当然这两个Promise不是同一个) async
函数内部会被await阻塞并按顺序执行代码逻辑
const promiseFactory = function(value) {
// Promise创建后立即执行里面的内容
return new Promise((resolve, reject) => {
console.log(value + ':开始执行异步代码...')
// for (const t = Date.now(); Date.now() - t <= 100000;) { } // 如果Promise中写同步代码那整个项目都会被阻塞(重要)
setTimeout(() => {
for (const t = Date.now(); Date.now() - t <= 3000;) { } // 阻塞3秒
resolve(value+':异步代码执行完毕.')
}, 1000)
})
}
async function test() {
console.log('开始测试')
const value1 = await promiseFactory(1) // 这里会阻塞
console.log(value1) // 类似 Promise.then()
const value2 = await promiseFactory(2)
console.log(value2) // 类似 Promise.then().then()
const value3 = await promiseFactory(3)
console.log(value3) // 类似 Promise.then().then().then()
console.log('测试结束')
}
test()
console.log('执行项目其他代码')
for (const t = Date.now(); Date.now() - t <= 3000;) { } // 阻塞3秒
console.log('其他代码执行完毕')
async异常捕获
第一种写法:
const myFunction = async function () {
try {
await something()
} catch (value) { // 类似 Promise.catch()
console.log(value)
}
}
第二种写法:
const myFunction = async function () {
await somethingPromise().catch( (value)=>{ console.log(value) } )
}