1.同步和异步是什么:
①同步:同步是指如果一个进程在执行某个请求的时候,如果该请求需要等待一段时间,那么该进程会一直等待下去,直到收到返回信息才继续执行下去
②异步: 指一个请求在执行某个请求的时候,即使该请求需要等待一段时间,该请求也不会阻塞下面的请求,而是一直执行下去
2.异步函数发展史
callback --> generator --> promise和then -->async和await
3.常见的异步方式:
①定时器 // setTimeout(fn,time) 、setInterval(fn,time)
②接口调用 //ajax
③事件函数 //事件监听事件
4.多次异步调用的结果:
①多次异步调用的结果会导致顺序可能不同步
②异步调用的结果如果存在依赖,则需要嵌套。当进行多次回调函数时,会出现回调地狱
5.Promise的概述:
①背景:JavaScript是单线程的,当执行一个请求,如果该请求的请求时间过长,该应用就会变得卡顿,那么让代码异步执行就变得很有必要了。
②介绍:
1. Promise是一个构造函数,接受一个参数作为对象,该函数的两个参数分别是 resolve和reject 。
2.Promise身上有all、reject、resolve这几个眼熟的方法,原型上有then、catch等同样很眼熟的方法。
3.promise有三种状态,pending、fulfilled、rejected,状态改变只能是pending->fulfilled或者 pending->rejected , 状态一旦改变则不能再变。(要想改变得用return返回一个异步函数)
③作用:
1.解决回调地狱的问题
2.链式调用
6.Promise的注意事项:
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('异步任务执行完成');
resolve('随便什么数据2');
}, 2000);
});
运行代码,会在2秒后输出“异步任务执行完成”。注意!我只是new了一个对象,并没有调用它,我们传进去的函数就已经执行了,这是需要注意的一个细节。所以我们用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数,如:
function runAsync(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('执行完成');
resolve('随便什么数据');
}, 2000);
});
return p;
}
runAsync()
7.Promise的基本用法:
(1)使用new实例化一个Promise对象,Promise的构造函数中传递一个参数。这个参数是一个函数,该函数用于处理异步任务。
(2)并且传入两个参数:resolve和reject,分别表示异步执行成功后的回调函数和异步执行失败后的回调函数;
(3)通过 promise.then() 处理返回结果。这里的 p 指的是 Promise实例。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
function getAsync(){
const promise = new Promise((resolve, reject) => {
// 这里做异步任务(比如ajax 请求接口。这里暂时用定时器代替)
setTimeout(function() {
// 接口返回的数据
var data = { retCode: 0, msg: 'qianguyihao' };
if (data.retCode == 0) {
// 接口请求成功时调用
resolve(data);
} else {
// 接口请求失败时调用
reject({ retCode: -1, msg: 'network error' });
}
}, 100);
});
return promise
}
// 第二步:业务层的接口调用。这里的 data 就是 从 resolve 和 reject 传过来的,也就是从接口拿到的数据
getAsync().then(data => {
// 从 resolve 获取正常结果
console.log(data);
}).catch(data => {
// 从 reject 获取异常结果
console.log(data);
});
</script>
</body>
</html>
8.Promise的链式用法
解释:链式用法就是当一个异步执行完毕再执行另外一个异步
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
function getAsync(param){
const promise = new Promise((resolve, reject) => {
// 这里做异步任务(比如ajax 请求接口。这里暂时用定时器代替)
setTimeout(function() {
// 接口返回的数据
var data = { retCode: 0, msg: 'qianguyihao' };
if (data.retCode == 0) {
// 接口请求成功时调用
resolve(data);
console.log(param);
} else {
// 接口请求失败时调用
reject({ retCode: -1, msg: 'network error' });
}
}, 1000);
});
return promise
}
// 第二步:业务层的接口调用。这里的 data 就是 从 resolve 和 reject 传过来的,也就是从接口拿到的数据
getAsync("1").then(data => {
// 从 resolve 获取正常结果
return getAsync("2")
}).then(data => {
// 从 resolve 获取正常结果
return getAsync("3")
})
</script>
</body>
</html>
9.promise链式用法的缺点:
对于不熟悉promise的人来说,简直就是噩梦,因此才出了这一篇博文,有不足之处望提出,会加以改进
10.Promise的常用api
1.Promise.all():并发处理多个异步任务,所有任务都执行成功,才能得到结果。
2.Promise.race(): 并发处理多个异步任务,只要有一个任务执行成功,就能得到结果。
###Promise.all()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script type="text/javascript">
/*
封装 Promise 接口调用
*/
function queryData(url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) return;
if (xhr.readyState == 4 && xhr.status == 200) {
// 处理正常结果
resolve(xhr.responseText);
} else {
// 处理异常结果
reject('服务器错误');
}
};
xhr.open('get', url);
xhr.send(null);
});
}
var promise1 = queryData('http://localhost:3000/a1');
var promise2 = queryData('http://localhost:3000/a2');
var promise3 = queryData('http://localhost:3000/a3');
Promise.all([promise1, promise2, promise3]).then(result => {
console.log(result);
});
</script>
</body>
</html>
###Promise.race
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script type="text/javascript">
/*
封装 Promise 接口调用
*/
function queryData(url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) return;
if (xhr.readyState == 4 && xhr.status == 200) {
// 处理正常结果
resolve(xhr.responseText);
} else {
// 处理异常结果
reject('服务器错误');
}
};
xhr.open('get', url);
xhr.send(null);
});
}
var promise1 = queryData('http://localhost:3000/a1');
var promise2 = queryData('http://localhost:3000/a2');
var promise3 = queryData('http://localhost:3000/a3');
Promise.race([promise1, promise2, promise3]).then(result => {
console.log(result);
});
</script>
</body>
</html>
11.async和await:
①背景:上文说到如果不熟悉promise,那么它的链式调用无异于看天书,这时候async和await就应运而生,很好的解决了这个问题。
②.什么是async和await:
async顾名思义是“异步”的意思,async用于声明一个函数是异步的。而await从字面意思上是“等待”的意思,就是用于等待异步完成。并且await只能在async函数中使用。
③async和await的作用:
比promise更直观,更具有可读性
12.async / await的基本用法:
通常async、await都是跟随Promise一起使用的。为什么这么说呢?因为async返回的都是一个Promise对象同时async适用于任何类型的函数上。这样await得到的就是一个Promise对象(如果不是Promise对象的话那async返回的是什么 就是什么,await得到Promise对象之后就等待Promise接下来的resolve或者reject。
async function testSync() {
2 const response = await new Promise(resolve => {
3 setTimeout(() => {
4 resolve("async await test...");
5 }, 1000);
6 });
7 console.log(response);
8 }
9 testSync();//async await test...
13.async / await的深入用法
1.async函数返回一个promise对象,如果在async函数中返回一个直接量,async会通过Promise.resolve封装成Promise对象。
我们可以通过调用promise对象的then方法,获取这个直接量。
async function test(){
return "Hello World";
}
var result=test();
console.log(result);
//打印Promise { 'Hello World' }
2.如果async函数不返回值:
async function test(){
}
var result=test();
console.log(result);
//打印Promise { undefined }
3.await的用法:
①说明:await会暂停当前async的执行,await会阻塞代码的执行,直到await后的表达式处理完成,代码才能继续往下执行。
await后的表达式既可以是一个Promise对象,也可以是任何要等待的值。
如果await等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
②注意事项:await堵塞非promise的异步函数是无效的
1.基本用法:
function A() {
return "Hello ";
}
async function B(){
return "World";
}
async function C(){
//等待一个字符串
var s1=await A();
//等待一个promise对象,await的返回值是promise对象resolve的值,也就是"World"
var s2=await B();
console.log(s1+s2);
}
C();
//打印"Hello World"
2.进阶用法:
1.async实现链式回调且没有定义then/catch实现的函数,默认走resolve(),而调用resolve默认会打印传进去的参数的值
结果: 输出11 一秒后再打印22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
async function testSync() {
console.log(await getAsync("11"));
console.log(await getAsync("22"));
}
function getAsync(param){
return new Promise((resolve,reject)=>{
setTimeout(function(){
resolve(param)
},1000)
})
}
testSync();
</script>
</body>
</html>
2.async实现链式回调但是定义了then/catch实现的函数,resolve默认输出的是undefined
结果: 输出 "11" undefined
一秒后输出: "22" undefined
<script>
//async的返回值是一个promise
async function testSync() {
console.log(await getAsync("11"));
console.log(await getAsync("22"));
}
function getAsync(param){
return new Promise((resolve,reject)=>{
setTimeout(function(){
resolve(param)
// reject(param)
},1000)
}).then(function(){
console.log(param);
}).catch(function(){
console.log("err");
})
}
testSync()
</script>
3.处理机制:
区别:函数里面多了个await
①串行处理
1 async function asyncAwaitFn(str) {
2 return await new Promise((resolve, reject) => {
3 setTimeout(() => {
4 resolve(str)
5 }, 1000);
6 })
7 }
8
9 const serialFn = async () => { //串行执行
10
11 console.time('serialFn')
12 console.log(await asyncAwaitFn('string 1'));
13 console.log(await asyncAwaitFn('string 2'));
14 console.timeEnd('serialFn')
15 }
16
17 serialFn();
###string 1
###string 2
###serialFn: 2007.7892ms
②并行处理:
1 async function asyncAwaitFn(str) {
2 return await new Promise((resolve, reject) => {
3 setTimeout(() => {
4 resolve(str)
5 }, 1000);
6 })
7 }
8 const parallel = async () => { //并行执行
9 console.time('parallel')
10 const parallelOne = asyncAwaitFn('string 1');
11 const parallelTwo = asyncAwaitFn('string 2')
12
13 //直接打印
14 console.log(await parallelOne)
15 console.log(await parallelTwo)
16
17 console.timeEnd('parallel')
18
19
20 }
21 parallel()
### string1
### string2
### parallel: 1009.3232
###比串行快了一倍
4.错误处理:
JavaScript异步请求肯定会有请求失败的情况,上面也说到了async返回的是一个Promise对象。既然是返回一个Promise对象的话那处理当异步请求发生错误的时候我们就要处理reject的状态了。在Promise中当请求reject的时候我们可以使用catch。为了保持代码的健壮性使用async、await的时候我们使用try catch来处理错误。
<script>
async function catchErr() {
try {
const errRes = await new Promise((resolve, reject) => {
setTimeout(() => {
reject("http error...");
}, 1000)}
)
}
catch(err) {
console.log(err);
}
}
catchErr(); //http error...
</script>
14.宏任务和微任务
1.概念: 宏任务和微任务表示异步任务的两种分类。常见的宏任务: setTimeout、setInterval, 常见的微任务: Promise、then、catch、finally
2.基本执行顺序 : 主线程(外层宏) --> 微 --> 宏
①基本示例1:
// 1 1.1 - 2 - 3
setTimeout(() => {
console.log('3')
}, 0)
console.log('1');
new Promise((resolve) => {
console.log('1.1');
resolve()
}).then(() => {
console.log('2');
}).then(()=>{
console.log('2.1')
})
②基本示例2:
console.log('1');
setTimeout(function () {
console.log('3');
new Promise(function (resolve) {
console.log('3.1');
resolve();
new Promise(function (resolve) {
console.log('9.1');
resolve();
}).then(function () {
console.log('9.2')
})
}).then(function () {
console.log('4')
})
})
new Promise(function (resolve) {
console.log('1.1');
resolve();
}).then(function () {
console.log('2')
})
setTimeout(function () {
console.log('5');
new Promise(function (resolve) {
console.log('5.1');
resolve();
}).then(function () {
console.log('6')
})
})
③基本示例3:
console.log('1');
setTimeout(function () {
console.log('3');
new Promise(function (resolve) {
console.log('3.1');
resolve();
new Promise(function (resolve) {
console.log('9.1');
resolve();
}).then(function () {
console.log('9.2')
})
}).then(function () {
console.log('4')
})
})
new Promise(function (resolve) {
console.log('1.1');
setTimeout(function(){
console.log("8");
})
resolve();
}).then(function () {
console.log('2')
})
setTimeout(function () {
console.log('5');
new Promise(function (resolve) {
console.log('5.1');
resolve();
}).then(function () {
console.log('6')
})
})
输出结果是: 1、1.1、2、3、3.1、9.1、9.2、4、8、5、5.1
3.宏任务和微任务的总结:
先执行主线程的东西,宏任务和微任务加入队列,主线程执行完之后,执行微任务队列(先进先出的原则),微任务如果包含宏任务,也先丢到宏任务后面,微任务执行完,最后执行宏任务
参考博客:
浙公网安备 33010602011771号