今天我们来复习promise的链式调用的使用方法,使用链式调用可以解决顺序执行异步操作。
promise链式调用
在JS中我们常用的链式调用应该是jQuery框架了,比如下面的代码
$('.level1').click(function () {
$(this)
.next()
.stop()
.slideToggle()
.parent()
.sibling()
.children('ul')
.slideUp();
});
Promise对象的实例方法then()、catch()、finally()其实会返回一个promise对象。
所以,可以在返回的promise对象上继续调用then()等实例方法,这种连续调用的方式称为promise链。
下面举个简单例子
创建一个promise对象,3秒后成功返回一个数字10。
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10);
}, 3 * 1000);
});
p.then((result) => {
console.log(result);
return result * 2;
});
// 3秒后输出:
// 10
在then()回调函数内我返回一个新值
result * 2
,因为then会返回一个新的promise对象,返回的值相当于resolve的值。所以可以接着继续调用then方法,像下面这样
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10);
}, 3 * 1000);
});
p.then((result) => {
console.log(result);
return result * 2;
}).then((result) => {
console.log(result);
return result * 3;
});
// 3秒后输出:
// 10
// 20
还可以接着继续then下去
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10);
}, 3 * 1000);
});
p.then((result) => {
console.log(result);
return result * 2;
}).then((result) => {
console.log(result);
return result * 3;
}).then((result) => {
console.log(result);
return result * 4;
});
// 3秒后输出:
// 10
// 20
// 60
多个then
不使用链式调用,同时在一个promise上调用多个then方法
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10);
}, 3 * 1000);
});
p.then((result) => {
console.log(result); // 10
return result * 2;
})
p.then((result) => {
console.log(result); // 10
return result * 3;
})
p.then((result) => {
console.log(result); // 10
return result * 4;
});
// 3秒后输出:
// 10
// 10
// 10
在上面例子中,一个promise对象,多个then,这些then之间没有任何关联。它们独立执行,不会像上面链式调用那样。
在实际项目中,很少会这样给一个promise调用多个then方法。
return new promise
上面我们在then的回调函数里去return一个值,其实就相当于去new一个promise然后立刻reslove该值。
像下面这样
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10);
}, 3 * 1000);
});
p.then((result) => {
console.log(result);
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(result * 2);
}, 3 * 1000);
});
}).then((result) => {
console.log(result);
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(result * 3);
}, 3 * 1000);
});
}).then(result => console.log(result));
// 每隔3秒后输出:
// 10
// 20
// 60
接着,我们可以封装一个函数,用来返回promise
// 生成promise
function generateNumber(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num);
}, 3 * 1000);
});
}
generateNumber(10)
.then(result => {
console.log(result);
return generateNumber(result * 2);
})
.then((result) => {
console.log(result);
return generateNumber(result * 3);
})
.then(result => console.log(result));
// 每隔3秒后输出:
// 10
// 20
// 60
和上面的结果一样,我们用generateNumber函数做了一个封装。
promise链式语法
在JS开发中,我们经常遇到需要按顺序去执行多个异步任务。下一个异步任务依赖上一个异步任务的结果。如果不用没有promise,使用callback将会是这样
Get('/user', function(user) {
Get(`/level/${user.id}`, function(level) {
Get(`/info/${level.id}`, function(info) {
console.log(info)
})
})
})
使用promise链式调用
getUser()
.then(result => getLevel(result))
.then(result => getInfo(result))
...
相比callback方式来说,看着会更加清晰,callback方式会无限的嵌套下去,promise只需依次向下继续then就可以了。
下面举个实际的例子,
- 从数据库中获取数据
- 获取该用户购物车中的商品
- 计算商品的总价
假设上面的操作都为异步操作,并且需要顺序执行
function getUser(userId) {
return new Promise((resolve, reject) => {
console.log('从数据库中获取用户信息');
setTimeout(() => {
resolve({
userId: userId,
username: 'admin'
});
}, 1000);
})
}
function getServiceCart(user) {
return new Promise((resolve, reject) => {
console.log(`获取 ${user.username} 的购物车商品`);
setTimeout(() => {
resolve([
{name: 'book', price: 10},
{name: 'phone', price: 200}
]);
}, 3 * 1000);
});
}
function getServiceCost(goods) {
return new Promise((resolve, reject) => {
console.log(`计算购物车商品的总价: ${JSON.stringify(goods)}.`);
setTimeout(() => {
resolve(goods.reduce((total, cur) => total + cur.price, 0));
}, 2 * 1000);
});
}
getUser(100)
.then(getServiceCart)
.then(getServiceCost)
.then(console.log);
// 输出:
// 从数据库中获取用户信息
// 获取 admin 的购物车商品
// 计算购车商品的总价: [{"name":"book","price":10},{"name":"phone","price":200}].
// 210
promise解决了回调地狱的问题,但仍不够优雅。
在ES2017中引入了async/await关键字,可以写出比promise更加简洁的代码,后面再写。
今天我们学习了promise用来处理多个异步任务的promise链式用法。
欢迎关注我公众号【小帅的编程笔记】,跟着我每天进步一点点