1 是什么
先直接上图,打印一下 Promise 对象,观察下 Promise 是什么
console.dir(Promise)
可以知道,Promise 是一个构造函数,有着 reject、resolve 函数。prototype 有 then、catch 等方法,说明了只要是 Promise 对象都会有这两个方法。
Promise 构造函数是传入一个函数
2 怎么用
var promise = new Promise((resolve, reject) => {
setTimeout(function () {
console.log(‘ 执行完成 ’);
resolve(‘ 随便什么数据 ’);
}, 2000);
});
result:
执行完成
传入的函数有两个参数:resolve、reject,分别表示异步操作执行成功后的回调函数和异步操作失败后的回调函数。其实这里用“成功”和“失败”来描述并不准确,按照标准来讲,resolve 是将 Promise 的状态置为 fullfiled,reject 是将 Promise 的状态置为 rejected。不过在我们开始阶段可以先这么理解,后面再细究概念。
运行以上代码,发现 promise 并没有调用,只是 new 了一个 Promise,对应的传入函数的代码就已经执行了。因此,我们通常都是把 Promise 包在一个函数中,在需要的时候才去运行这个函数,如:
function f() {
var promise = new Promise((resolve, reject) => {
setTimeout(function () {
console.log(‘ 执行完成 ’);
resolve(‘ 随便什么数据 ’);
}, 2000);
});
return promise
}
f()
result:
执行完成
这时引出两个问题。为什么需要大费周章的包装这样的一个函数?resolve 有什么用?
2.1 为什么需要引入
f().then(function(data){
console.log(data);
// 后面可以用传过来的数据做些其他操作
//……
});
在 f() 的返回上可以直接调用 then 方法,then 接收一个参数,是函数,并且会拿到我们在 f 中调用 resolve 时传入的参数。运行这段代码,会在 2 秒后输出“执行完成”,紧接着输出“随便什么数据”。
这时,我们恍然大悟了,原来 then 调用的 function 相当于我们之前写的回调函数,相当于是这种形式:
function runAsync(callback){
setTimeout(function(){
console.log(‘ 执行完成 ’);
callback(‘ 随便什么数据 ’);
}, 2000);
}
runAsync(function(data){
console.log(data);
});
那使用 Promise 有什么好处呢?我们不妨考虑一个问题,如果 callback 函数也是一个异步操作,而且执行完后也需要有相应的回调函数,可能会变成 runAsync(function(data,function(data){}){…..})。看起来十分丑陋,假如使用 Promise 代码就不会这么丑陋了~
Promise 的优势在于,可以在 then 方法中继续写 Promise 对象并返回,然后继续调用 then 来进行回调操作。
2.2 链式操作的用法
所以,从表面上看,Promise 只是能够简化层层回调的写法,而实质上,Promise 的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递 callback 函数要简单、灵活的多。所以使用 Promise 的正确场景是这样的:
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return runAsync3();
})
.then(function(data){
console.log(data);
});
function runAsync1(){
var p = new Promise(function(resolve, reject){
// 做一些异步操作
setTimeout(function(){
console.log(‘ 异步任务 1 执行完成 ’);
resolve(‘ 随便什么数据 1 ’);
}, 1000);
});
return p;
}
function runAsync2(){
var p = new Promise(function(resolve, reject){
// 做一些异步操作
setTimeout(function(){
console.log(‘ 异步任务 2 执行完成 ’);
resolve(‘ 随便什么数据 2 ’);
}, 2000);
});
return p;
}
function runAsync3(){
var p = new Promise(function(resolve, reject){
// 做一些异步操作
setTimeout(function(){
console.log(‘ 异步任务 3 执行完成 ’);
resolve(‘ 随便什么数据 3 ’);
}, 2000);
});
return p;
}
result:
异步任务 1 执行完成
随便什么数据 1
异步任务 2 执行完成
随便什么数据 2
异步任务 3 执行完成
随便什么数据 3
我们可以通过链式编程避免了包含多函数回调的情况,当然并不是都需要 return Promise 对象,你也可以返回其他数据类型,在后面的 then 中就能直接直接接收到数据了,比如:
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return ‘ 直接返回数据 ’; // 这里直接返回数据
})
.then(function(data){
console.log(data);
});
result:
异步任务 1 执行完成
随便什么数据 1
异步任务 2 执行完成
随便什么数据 2
直接返回数据
2.3 reject 用法
前面我们一直都是使用 resolve 函数,并没有使用 reject 函数。resolve 函数是表示了“执行成功”的回调,reject 函数表示“执行失败”的函数。
function runAsync1() {
var p = new Promise(function (resolve, reject) {
// 做一些异步操作
setTimeout(function () {
console.log(‘ 异步任务 1 执行完成 ’);
// resolve(‘ 随便什么数据 1 ’);
reject(“error”)
}, 1000);
});
return p;
}
runAsync1().then((data) => {
console.log(‘resolve’);
console.log(data)
}, (error, data) => {
console.log(‘rejected’);
console.log(error)
})
result:
异步任务 1 执行完成
rejected
error
需要注意的是,只要执行 reject 或者 resolve,后面的就不会执行了,如:
function runAsync1() {
var p = new Promise(function (resolve, reject) {
// 做一些异步操作
setTimeout(function () {
console.log(‘ 异步任务 1 执行完成 ’);
resolve(‘ 随便什么数据 1 ’);
reject(“error”)
}, 1000);
});
return p;
}
因为 resolve 在 reject 的前面,所以只会执行 resolve,不会执行 reject。
3 总结
Promise 是一个对象
引入是为了消除多重回调函数
Promise 还有很多方法没有介绍(catch、finally、success、fail)
参考文献
ECMAScript6 入门大白话讲解 Promise(一)ECMAScript6 入门
署名
广州芦苇科技 Java 开发团队
芦苇科技 - 广州专业互联网软件服务公司
抓住每一处细节,创造每一个美好
关注我们的公众号,了解更多
想和我们一起奋斗吗?lagou 搜索“芦苇科技”或者投放简历到 server@talkmoney.cn 加入我们吧
关注我们,你的评论和点赞对我们最大的支持