目录
一、先来个栗子 🌰
首先来看个需求 :
向服务器发送网络请求获取数据,一共需要发送三次请求
- 第一次请求
- 第二次的请求url依赖于第一次的结果;
- 第三次的请求url依赖于第二次的结果;
- 依次类推
0. 发送请求的函数
// 模拟发送请求,真实情况肯定是不同的请求,这里只使用这一个,道理一样
function requestHttp(url) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(url);
}, 2000);
});
}
1. 使用 回调函数
// 第一次请求
requestHttp('http://').then((res1) => {
console.log('res1', res1); // res1 http://
// 第二次请求
requestHttp(res1 + 'www').then((res2) => {
console.log('res2', res2); // res2 http://www
// 第三次请求
requestHttp(res2 + '.baidu.').then((res3) => {
console.log('res3', res3); // res3 http://www.baidu.
// 第四次请求
requestHttp(res3 + 'com').then((res4) => {
console.log('res4', res4); // res4 http://www.baidu.com
});
});
});
});
2. 使用 Promise
// 第一次请求
requestHttp('http://')
.then((res1) => {
console.log('res1', res1); // res1 http://
// 第二次请求
return requestHttp(res1 + 'www');
})
.then((res2) => {
console.log('res2', res2); // res2 http://www
// 第三次请求
return requestHttp(res2 + '.baidu.');
})
.then((res3) => {
console.log('res3', res3); // res3 http://www.baidu.
// 第四次请求
return requestHttp(res3 + 'com');
})
.then((res4) => {
console.log('res4', res4); // res4 http://www.baidu.com
});
3. 使用 Generator
01 - 普通
// 1. 定义生成器函数
function* excuteCode() {
const res1 = yield requestHttp('http://');
console.log('res1', res1); // res1 http://
const res2 = yield requestHttp(res1 + 'www');
console.log('res2', res2); // res2 http://www
const res3 = yield requestHttp(res2 + '.baidu.');
console.log('res3', res3); // res3 http://www.baidu.
const res4 = yield requestHttp(res3 + 'com');
console.log('res4', res4); // res4 http://www.baidu.com
}
// 2. 拿到生成器对象
const excuteIterator = excuteCode();
// 3. 执行,把第一个请求的promise返回了出来,是promise
excuteIterator.next().value.then((res1) => {
// 4. 调用next()继续执行,同时把第一个请求的结果传入,把第二个请求的promise返回了出来
excuteIterator.next(res1).value.then((res2) => {
excuteIterator.next(res2).value.then((res3) => {
excuteIterator.next(res3).value.then((res4) => {
excuteIterator.next(res4);
});
});
});
});
02 - 优化,自动
// 1. 定义生成器函数
function* excuteCode() {
const res1 = yield requestHttp('http://');
console.log('res1', res1); // res1 http://
const res2 = yield requestHttp(res1 + 'www');
console.log('res2', res2); // res2 http://www
const res3 = yield requestHttp(res2 + '.baidu.');
console.log('res3', res3); // res3 http://www.baidu.
const res4 = yield requestHttp(res3 + 'com');
console.log('res4', res4); // res4 http://www.baidu.com
}
// 2. 封装自动执行函数,把生成器函数传入
function autoExcute(generatorFn) {
// 3. 拿到生成器对象
const excuteIterator = generatorFn();
// 4. 定义递归函数,一次一次调用next方法
function deepFn(url = '') {
// { done: true/false, value: 值/undefined }
const result = excuteIterator.next(url);
// 执行完成时,done为true,直接返回
if (result.done) {
return;
}
// 没执行完,继续执行
result.value.then((url) => {
deepFn(url);
});
}
// 5. 执行递归函数
deepFn();
}
// 6. 开始执行函数
autoExcute(excuteCode);
4. 使用 Async + Await
// 1. 定义执行的函数
async function excuteCode() {
// 2. 第一次请求
const res1 = await requestHttp('http://');
console.log('res1', res1); // res1 http://
// 3. 第二次请求
const res2 = await requestHttp(res1 + 'www');
console.log('res2', res2); // res2 http://www
// 3. 第三次请求
const res3 = await requestHttp(res2 + '.baidu.');
console.log('res3', res3); // res3 http://www.baidu.
// 4. 第三次请求
const res4 = await requestHttp(res3 + 'com');
console.log('res4', res4); // res4 http://www.baidu.com
}
// 5. 执行代码
excuteCode();
二、异步函数 async
async关键字用于声明一个异步函数
async : 是asynchronous单词的缩写,异步、非同步
sync : 是synchronous单词的缩写,同步、同时
1. 异步函数的写法
写法一
async function foo() {}
foo();
写法二
const bar = async function() {}
写法三
const baz = async () => {}
写法四
class Person {
async running() {}
}
2. 异步函数的返回值
异步函数的返回值 : 一定是一个promise对象
异步函数的返回值 : 一定是一个promise对象
01 - 返回普通值
异步函数的返回值相当于被包裹到Promise.resolve,相当于 => Promise.resolve( 普通值 )
async function foo() {
// 相当于return Promise.resolve([1])
return [1];
}
foo().then((res) => {
console.log(res);
});
02 - 返回一个promise
如果返回值是Promise,状态由会由返回的Promise决定
async function foo() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('冲冲冲');
}, 3000);
});
}
foo().then((res) => {
console.log(res);
});
03 - 返回thenable对象
返回值是一个对象并且实现了thenable,那么会由对象的then方法来决定
async function foo() {
return {
then(resolve) {
setTimeout(() => {
resolve('无敌');
}, 5000);
}
};
}
foo().then((res) => {
console.log(res);
});
3. 异步函数的异常
如果在async中抛出了异常,那么程序它并不会像普通函数一样报错,这个异常不会被立即浏览器处理
而是会作为Promise的reject来传递 => Promise.reject(error)
01 - 使用Promise.reject
async function foo() {
console.log('111');
// 1. 抛出异常
return new Promise((_, reject) => {
reject('err');
});
}
foo()
.then((res) => {
console.log(res);
})
.catch((err) => {
// 2. 这里可以捕获
console.log(err);
});
02 - 抛出错误
async function foo() {
console.log('111');
// 1. 抛出异常
throw new Error('我错了');
console.log('222');
}
foo()
.then((res) => {
console.log(res);
})
.catch((err) => {
// 2. 这里可以捕获
console.log('oh', err);
});
三、异步函数中的 await
1. await 的使用
async函数可以在它内部使用await关键字,而普通函数中是不可以的
await后续返回一个Promise, 那么会等待Promise有结果之后, 才会继续执行后续的代码
function bar() {
console.log('bar function');
return new Promise((resolve) => {
setTimeout(() => {
resolve(123);
}, 2000);
});
}
// await条件: 必须在异步函数中使用
async function foo() {
console.log('-------');
// await后续返回一个Promise, 那么会等待Promise有结果之后, 才会继续执行后续的代码
const res1 = await bar();
console.log('await后面的代码:', res1);
const res2 = await bar();
console.log('await后面的代码:', res2);
console.log('-------');
}
foo();
2. await 处理异步请求
01 - resolve
function requestData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(url)
}, 2000);
})
}
async function getData() {
// 等待两秒,拿到url的值后才会继续执行
const res1 = await requestData("coder")
console.log("res1:", res1)
// 这里同样等待两秒
const res2 = await requestData(res1 + "star")
console.log("res2:", res2)
}
02 - reject
function requestData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
// 1. 如果发生报错
reject('error message');
}, 2000);
});
}
// 2. 如果发生报错
async function getData() {
try {
const res1 = await requestData('coder');
console.log('res1:', res1);
const res2 = await requestData(res1 + 'star');
console.log('res2:', res2);
} catch (error) {
// 01 - 这里可以捕获
console.log('err:', error);
}
}
// 02 - 这里也可以捕获
getData().catch((error) => {
console.log('err:', err);
});
3. await后面的值
通常await后面会跟上一个表达式 :
- 如果await后面是一个普通的值,那么会直接返回这个值
- 如果await后面是一个thenable的对象,那么会根据对象的then方法调用来决定后续的值
- 如果await后面的表达式,返回的Promise是resolve的状态,那么会将这个resolve的结果直接作为函数的Promise的resolve值
- 如果await后面的表达式,返回的Promise是reject的状态,那么会将这个reject结果直接作为函数的Promise的reject值
- 如果一直没有返回值,await就会一直等在那里,直到永远
await会等,等待有个人把值给它!!!
就像生成器函数中的,yield
01 - 普通
async function foo() {
// 一般不这么写,和直接赋值没有区别
const a = await 123;
console.log(a); // 123
}
console.log('foo', foo()); // foo Promise { <pending> }
02 - thenable对象
async function foo() {
// 等待两秒后拿到值
const a = await {
then(resolve) {
setTimeout(() => {
resolve(123);
}, 2000);
}
};
console.log(a); // 123
}
console.log('foo', foo()); // foo Promise { <pending> }
03 - promise.resolve
async function foo() {
// 等待两秒
const a = await new Promise((resolve) => {
setTimeout(() => {
resolve(123);
}, 2000);
});
console.log(a); // 123
}
console.log('foo', foo()); // foo Promise { <pending> }
04 - promise.reject
async function foo() {
// 等待两秒
const a = await new Promise((resolve, reject) => {
setTimeout(() => {
reject(123);
}, 2000);
});
// 这里不会运行,直接一层一层往上抛错误,因为异步函数返回值为promise,所以可以在外面捕获
console.log(a);
}
foo().catch((err) => {
console.log(err);
});
4. await 和 async 结合
因为异步函数的返回值一定是个promise,所以await会等待该promise的状态确定
一旦确定下来,就会拿到相当于是promise.then中解析出的值
// 1.普通函数返回promise对象
function requestData(url) {
console.log('request data');
return {
then(resolve) {
setTimeout(() => {
resolve(987);
}, 2000);
}
};
}
// 2. 异步函数返回普通的值
async function test() {
console.log('test function');
// 相当于返回的是 Promise.resolve('test')
return 'test';
}
// 3. 异步函数|普通函数返回promise对象
async function bar() {
console.log('bar function');
return new Promise((resolve) => {
setTimeout(() => {
resolve('bar');
}, 2000);
});
}
// 4. 异步函数|普通函数返回thenable对象
async function demo() {
console.log('demo function');
return {
then: function (resolve) {
resolve('demo');
}
};
}
// 3.调用的入口async函数
async function foo() {
console.log('foo function');
// 直接返回值
const res1 = await requestData('coder');
console.log('res1:', res1);
// 直接返回值
const res2 = await test();
console.log('res2:', res2);
// 会等待promise的状态确定后,才继续执行
const res3 = await bar();
console.log('res3:', res3);
// 会等待thenable的状态确定后,才继续执行
const res4 = await demo();
console.log('res4:', res4);
}
// 执行
foo();