深入JavaScript Day23 - promise+gen
2022-01-25 本文已影响0人
望穿秋水小作坊
有如下需求
用【setTimeout】模拟网络请求
①传入 【why】返回【res1】
②传入 【res1 + "aaa"】返回【res2】
③传入 【res2 + "bbb"】返回【res3】
④传入 【res3 + "ccc"】返回【res4】
一、异步代码的处理方案
1、【方案一】原始调用
function requestData(params) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(params);
}, 2000);
});
}
requestData("why").then((res1) => {
console.log(res1);
requestData(res1 + "aaa").then((res2) => {
console.log(res2);
requestData(res2 + "bbb").then((res3) => {
console.log(res3);
requestData(res3 + "ccc").then((res4) => {
console.log(res4);
});
});
});
});
- 这就是传说中的【回调地域,Callback Hell】
2、【方案二】利用then函数返回promise,自动调用的特性,把回调放到外层,形成链式调用
requestData("why")
.then((res1) => {
console.log(res1);
return requestData(res1 + "aaa");
})
.then((res2) => {
console.log(res2);
return requestData(res2 + "bbb");
})
.then((res3) => {
console.log(res3);
return requestData(res3 + "ccc");
})
.then((res4) => {
console.log(res4);
});
- 这种方式避免了【回调地域】,但是这种链式调用,阅读性还是比较差的
3、【方案三】借助Generator结合Promise,把函数调用转换成普通代码逻辑
function* createGenerator(parmas) {
const res1 = yield requestData(parmas);
console.log(res1);
const res2 = yield requestData(res1 + "aaa");
console.log(res2);
const res3 = yield requestData(res2 + "bbb");
console.log(res3);
const res4 = yield requestData(res3 + "ccc");
console.log(res4);
}
const generator = createGenerator("why");
generator.next().value.then((res1) => {
generator.next(res1).value.then((res2) => {
generator.next(res2).value.then((res3) => {
generator.next(res3).value.then((res4) => {
console.log(res4);
});
});
});
});
4、【方案四】对方案三进行优化
function* createGenerator() {
const res1 = yield requestData("why");
console.log(res1);
const res2 = yield requestData(res1 + "aaa");
console.log(res2);
const res3 = yield requestData(res2 + "bbb");
console.log(res3);
const res4 = yield requestData(res3 + "ccc");
console.log(res4);
return res4;
}
function execGenerator(genFn) {
const generator = genFn();
function exec(res) {
const result = generator.next(res);
if (result.done === true) {
return result.value;
} else {
result.value.then((res1) => {
exec(res1);
});
}
}
exec();
}
execGenerator(createGenerator);
- 上面的代码中,出了createGenerator代码,其他代码就可以自动化处理了
- 之前有个TJ前端大神,开发的
co
库,也就是这个原理
5、【方案五】借助ES8 的 async、await 关键字
- 要理解本质其实就是上面【方案四】Promise+Generator的实现,所以理解【方案四】非常重要
- 【注意】本质上还是Promise嵌套调用函数,只是嵌套代码由系统内部实现了。
async function getData() {
const res1 = await requestData("why");
console.log(res1);
const res2 = await requestData(res1 + "aaa");
console.log(res2);
const res3 = await requestData(res2 + "bbb");
console.log(res3);
const res4 = await requestData(res3 + "ccc");
console.log(res4);
return res4;
}
getData();
二、学习await、async
1、async是什么的缩写?
- asynchronize 异步的含义
- synchronize 同步的含义
2、下面代码的执行顺序?仅仅加上async关键字的函数和普通函数执行有区别吗?
async function foo() {
console.log("内部的代码执行-1");
}
console.log("script start-2");
foo();
console.log("script end-3");
- 【代码执行顺序如下】
script start-2
内部的代码执行-1
script end-3
- 【仅仅加上async关键字的函数和普通函数没有区别】
3、观察如下代码,async关键字的函数返回值是什么?
async function foo() {
console.log("内部的代码执行-1");
}
const result = foo();
result.then((res) => {
console.log("返回值是Promise吗?", res);
});
console.log(result);
- 【async的返回值是一个promise】
- 【上述代码执行顺序如下】
内部的代码执行-1
Promise {<fulfilled>: undefined}
返回值是Promise吗? undefined
4、观察如下代码,如果async函数内部抛出异常会处理?
async function foo() {
console.log("内部的代码执行-1");
throw new Error("async error");
console.log("内部的代码执行-2");
}
const result = foo();
result
.then((res) => {
console.log("返回值是", res);
})
.catch((err) => {
console.log("异常信息是", err);
});
- 【代码执行结果如下】
- 【async函数内部抛出异常会被当成reject抛出】
内部的代码执行-1
index.js:14 异常信息是 Error: async error
at foo (index.js:3:9)
at index.js:7:16
5、【最重要】观察如下代码,await代码执行顺序?
function requestData(params) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(params);
resolve(params);
}, 2000);
});
}
async function foo() {
await requestData("why1");
await requestData("why2");
console.log("why4");
}
const result = foo();
- 【打印结果 why1、why2、why4】
- 要把【why2、why4】看成【why1.then】的结果
- 要把【why4】看成【why2.then】的结果
- 【本质】自身理解:其实async、await本质还是嵌套调用函数的语法糖