比如说有这么个场景:首先请求登陆接口,返回{username: '小明'}。然后请求获取额度接口,返回{amt:3434}。将amt展示在页面上
1、最开始的回调
这是最开始的写法,一层一层的回调
$.post('login.do', res => {
let username = res.username;
$.get('getAmt.do?username' + username, res => {
console.log('页面渲染可用金额', res.amt);
});
});
2、promise写法
Promise出来后的写法
$.post('login.do').then(res => {
let username = res.username;
$.get('getAmt.do?username' + username).then(res => {
console.log('页面渲染可用金额', res.amt);
});
});
2、generator+yield的写法
generator+yield是es6提出的。
(1)什么是generator
generator其实也是就是一个函数,在function和函数名之间有*,表示这是一个generator函数,generator函数直接调用返回的是一个generator对象,不会立即执行函数里面的内容,当调用next()后才会执行,每一个next()都执行到yield
比如下面代码:
function* g() {
yield '这是a';
yield '这是b';
yield '这是c';
return '这是return';
}
var gen = g();
gen.next(); // 返回Object {value: "这是a", done: false}
gen.next(); // 返回Object {value: "这是b", done: false}
gen.next(); // 返回Object {value: "这是c", done: false}
gen.next(); // 返回Object {value: "这是return", done: true}
gen.next(); // 返回Object {value: undefined, done: true}
其中next的返回值,value值是yield后面的值,done表示是否还有下一层可以执行。
如果最后没有return '这是return',即代码如下
function* g() {
yield '这是a';
yield '这是b';
yield '这是c';
}
var gen = g();
gen.next(); // 返回Object {value: "这是a", done: false}
gen.next(); // 返回Object {value: "这是b", done: false}
gen.next(); // 返回Object {value: "这是c", done: false}
gen.next(); // 返回Object {value: undefined, done: true}
可以看到最后的next也是返回done=true。
(2)yield的返回值
yield的右边会作为返回值返回到next()的value,可以是字符串、布尔型、json、promise等任何数据
(3)yield的参数
next()的参数会作为yield的左边值传递过去。
function* g(str1) {
console.log('str1', str1); // str1: 参数1
let str2 = yield str1 + 'a';
console.log('str2', str2); // str2: 参数3
let str3 = yield str1 + 'b';
console.log('str3', str3); // str3: 参数4
}
var gen = g('参数1'); // 这里传达了str1
gen.next('参数2'); // 第一个next是无法传参过去的,没有什么意义
gen.next('参数3');
gen.next('参数4');
具体执行过程如下图

除了第一次next外,每一次next的参数,会赋值给yield左边的变量。
(4)改造promise
generator + promise可以实现同步写法实现异步逻辑,代码如下:
function* g(username, password) {
let res1 = yield $.post('login.do', {username, password});
let res2 = yield $.post('getAmt.do', {username: res1.username});
console.log('res2的结果', res2);
return false;
}
var gen = g('小明', '小明密码');
gen.next().value.then(res => { // value是$.post的promise,请求login.do
gen.next(res).value.then(res => { // 请求getAmt.do
gen.next(res); // 请求getAmt.do后的处理
});
});
从上面看护,在generator函数里面,大体已经像同步的写法了,但是对面gen又陷入了回调的地狱。
3、封装好runner函数库
不难发现,对于generator的调用,实际上就是一个递归,当发现done=false就执行next(),否则就说明递归结束了
// 封装runner
function runner (fn) {
var o = fn();
_next();
function _next(lastResult) {
let result = o.next(lastResult);
if (!result.done) {
// done=false表示还没有结束,进入下一轮递归
result.value.then(res => {
_next(res);
});
} else {
// done=true表示最后一轮,结束递归
}
}
}
runner(function *() {
let res1 = yield $.get('/1.json', {username: '小明', password: '小明密码'});
console.log('res1', res1);
let res2 = yield $.get(`/2.json?login=${res1.name}`);
console.log('页面渲染金额', res2.amt);
return false;
});
封装好runner后,页面上只需要引入,并关注与里面业务代码逻辑就可以,实际上这个也是async+await的雏形
4、async+await
到了es7,推出了async+await,async相当于上面的runner方法,await相当于上面的yield关键词
async function g (username, passowrd) {
let res1 = await $.get('/1.json', {username: '小明', password: '小明密码'});
console.log('res1', res1);
let res2 = await $.get(`/2.json?login=${res1.name}`);
console.log('页面渲染金额', res2.amt);
}
g('小明', '小明密码');
这种就可以用同步写法实现异步操作