async是asynchronous单词的缩写,异步、非同步;
: async,会在 HTML 文档解析时并行下载文件,并在下载完成后立即执行(暂停 HTML 解析)。
async关键字用于声明一个异步函数, async异步函数可以有很多中写法
// 异步函数
// 写法一
async function foo() {
console.log("foo function1")
}
// 写法二
const bar = async function() {}
// 写法三
const baz = async () => {}
// 写法四
class Person {
async running() {}
}
异步函数的内部代码执行过程和普通的函数是一致的,默认情况下也是会被同步执行。
// 返回值的区别
// 1.普通函数
function foo1() {
return 123
}
console.log(foo1()) //123
异步函数有返回值时,和普通函数会有区别:
// 2.异步函数
async function foo2() {
// 1.返回一个普通的值。被包裹到Promise.resolve
// -> Promise.resolve(321)
return ["abc", "cba", "nba"] //res: (3) ['abc', 'cba', 'nba']
// 2.返回一个Promise。状态由由Promise决定
// return new Promise((resolve, reject) => {
// setTimeout(() => {
// resolve("aaa") //res: aaa
// }, 3000)
// })
// 3.返回一个thenable对象。由对象的then方法来决定
// return {
// then: function(resolve, reject) {
// resolve("bbb") //res: bbb
// }
// }
}
foo2().then(res => {
console.log("res:", res) //res: (3) ['abc', 'cba', 'nba']
})
// 如果异步函数中有抛出异常(产生了错误), 这个异常不会被立即浏览器处理
// 进行如下处理: Promise.reject(error)
async function foo() {
console.log("---------1")
console.log("---------2")
// "abc".filter()
throw new Error("coderhhh async function error")
console.log("---------3")
return 123
}
// promise -> pending -> fulfilled/rejected
foo().then(res => {
console.log("res:", res)
}).catch(err => {
console.log("coderhhh err:", err)
console.log("继续执行其他的逻辑代码")
})
async函数特殊之处是可以在它内部使用await关键字,而普通函数中是不可以的。
await关键字的特点?
如果await后面是一个普通的值,会直接返回这个值;
如果await后面是一个thenable的对象,会根据对象的then方法调用来决定后续的值;
如果await后面的表达式,返回的Promise是reject的状态,那么会将这个reject结果直接作为函数的Promise的reject值;
// await条件: 必须在异步函数中使用
function bar() {
console.log("bar function")
return new Promise(resolve => {
setTimeout(() => {
resolve(123)
}, 2000) //等待2秒钟
})
}
async function foo() {
console.log("-------")
// await后续返回一个Promise, 那么会等待Promise有结果之后, 才会继续执行后续的代码
const res1 = await bar()
console.log("await后面的代码:", res1) //await后面的代码: 123【等待2秒后,才打印】
const res2 = await bar()
console.log("await后面的代码:", res2)// await后面的代码: 123 【等待2秒后,才打印
console.log("+++++++")//+++++++
}
foo()
await处理异步请求
function requestData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(url)
// reject("error message") //如果返回的Promise状态是reject,浏览器的控制台会抛出异常信息
}, 2000);
})
}
async function getData() {
const res1 = await requestData("hhh") //返回的Promise状态是resolve,则继续往下执行代码
console.log("res1:", res1)
const res2 = await requestData(res1 + "kobe")
console.log("res2:", res2)
}
getData().catch(err => { //如果返回的Promise状态是reject,浏览器的控制台会抛出异常信息
console.log("err:", err) //err: error message
})
线程和进程是操作系统中的两个概念:进程>>线程
听起来很抽象,解释:
一个形象的例子解释:
如何同时让多个进程(边听歌、边写代码、边查阅资料)同时工作?
可以在Mac的活动监视器或者Windows的资源管理器中查看到很多进程:
JavaScript是单线程的,但是JavaScript的线程应该有自己的进程:浏览器或者Node。
浏览器是一个进程吗,它里面只有一个线程吗?
JavaScript的代码执行是在一个单独的线程中执行的:
所以真正耗时的操作,实际上并不是由JavaScript线程在执行的:
在执行JavaScript代码的过程中,有异步操作?
事件循环中并非只维护着一个队列,事实上是有两个队列:
事件循环对于两个队列的优先级?
给出下列代码的正确执行顺序(也就是打印顺序),先自己理解了,再看代码运行结果。
console.log("script start")
setTimeout(function () {
console.log("setTimeout1");
new Promise(function (resolve) {
resolve();
}).then(function () {
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then4");
});
console.log("then2");
});
});
new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () {
console.log("then1");
});
setTimeout(function () {
console.log("setTimeout2");
});
console.log(2);
queueMicrotask(() => {
console.log("queueMicrotask1")
});
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then3");
});
console.log("script end")
【详细解题】找出对应main javascrip、微任务、宏任务的代码。
[[main javascrip]](由上到下的代码排列)
console.log("script start");
new Promise(function (resolve) {
console.log("promise1");
resolve();
})
console.log(2);
new Promise(function (resolve) {
resolve();
})
console.log("script end")
[[微任务]]
Promise.then()
queueMicrotask()
[[宏任务]]
setTimeout() 所以该函数内的promise启动需等待该函数执行后
【详细解题】【具体的逻辑】
(1)先把main javascrip代码执行完毕,即打印出的前四行。
script start
promise1
2
script end
(2)执行微任务的代码。
除去已经打印的main javascrip,因为setTimeout()属于宏任务,故跳过。
根据代码的自上而下的执行顺序,于是进入第一个微任务内部代码,如下所示。
new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () {
console.log("then1");
});
老样子,在这个微任务内部,继续按照main javascrip、微任务、宏任务的顺序执行。
由于console.log("promise1");已经执行过,继续往下。
即来到.then()方法内,于是执行console.log("then1"),打印then1,至此该微任务执行完毕。
继续往下,来到下面第二个微任务内部代码。
由于只有内部简单,故执行 console.log("queueMicrotask1"),打印queueMicrotask1。
queueMicrotask(() => {
console.log("queueMicrotask1")
});
继续往下,来到下面第三个微任务内部代码,老样子,按照main javascrip、微任务、宏任务的顺序。
找main JavaScript,无打印代码,这个微任务内的main JavaScript执行完毕。
下一步轮到微任务,即进入.then()内部,执行console.log("then3"),打印then3。
至此第三个微任务全部代码执行结束。
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then3");
});
所有的微任务执行完毕,可进行宏任务的执行。
(3)来到宏任务。
按照代码由上而下的顺序,来到第一个宏任务内部。
setTimeout(function () {
console.log("setTimeout1");
new Promise(function (resolve) {
resolve();
}).then(function () {
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then4");
});
console.log("then2");
});
});
先找到main JavaScript的打印代码并执行。
可见,在该宏任务内部,含有.then()方法的,都属于微任务,跳过。
所以mai该宏任务内的nJavaScript即可打印setTimeout1和then2。
轮到微任务执行。进入.then()方法,打印then4。该函数执行完毕。
往下走,来到下面第二个的宏任务。内部不复杂,直接打印setTimeout2。
setTimeout(function () {
console.log("setTimeout2");
});
加入了异步函数,考察对await的理解
async function async1 () {
console.log('async1 start')
await async2();
console.log('async1 end')
}
async function async2 () {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('setTimeout')
}, 0)
async1();
new Promise (function (resolve) {
console.log('promise1')
resolve();
}).then (function () {
console.log('promise2')
})
console.log('script end')
【详细解题】找出对应main javascrip、微任务、宏任务的代码。
[[main javascrip]](由上到下的代码排列)
console.log('script start')
async1();
console.log('script end')
[[微任务]]
async2 ()
[[宏任务]]
setTimeout() 虽然该函数的时间设置为0,但是在宏任务定义范围内,是宏任务,就必须等前面两项main JavaScript和微任务执行完毕。
【详细解题】【具体的逻辑】
(1)先把main javascrip代码执行。
前面两个函数async function async1 ()、async function async2 () 只声明,但未执行,故打印script start。
遇到setTimeout()函数,直接归类到宏任务队列。
进入async1()函数,打印 async1 start。
async1()函数由于遇到await,先执行async2()函数,故打印async2。
但需要将其后代码console.log('async1 end')加入到微任务队列。
至此,async1()函数执行完毕
遇到new Promise()函数,进入执行console.log('promise1'),故打印promise1。
遇到new Promise()函数的.then()函数,添加到微任务队列。
至此,new Promise()函数执行完毕。
遇到console.log('script end'),打印script end。
至此,main JavaScript执行完毕。
(2)执行微任务的代码。查看微任务的队列,如下先后顺序排列。
console.log('async1 end')
new Promise().then (function () {
console.log('promise2')
}
微任务队列,按照先来后到顺序,先打印async1 end,后打印.then()函数内的promise2。
至此,所有微任务执行完毕。
(3)执行宏任务。
宏任务只有一个 setTimeout()函数,不复杂,直接打印setTimeout。
setTimeout(function () {
console.log('setTimeout')
}, 0)
如果函数内部使用try..catch..final
格式捕捉异常,可以让代码继续执行,不会中断
function test() {
// 自己捕获了异常的话, 那么异常就不会传递给浏览器, 那么后续的代码可以正常执行
try {
foo()
console.log("try后续的代码")
} catch(error) {
console.log("catch中的代码")
// console.log(error)
} finally {
console.log("finally代码")
}
}
function bar() {
test()
}
bar()