对比回调函数和Promise
暂时不管Promise是什么,先看一下下面的代码,看一看Promise的好处。需要特别说明的是,在这个对比的中,Promise和回调都没有考虑存在异常的情况。
回调函数执行一次
首先,定义一个回调函数,调用一次,看看这个代码的写法。
'use strict';
// 定义一个计数器,用来统计回调函数执行的次数
let count = 1;
/**
* 定义一个异步执行函数,参数是回调函数
*/
function asyncFunc(callback) {
setTimeout(function () {
callback(`callback ... 执行了 ${count} 次`);
count++;
}, 1000);
};
// 调用函数
asyncFunc(function(data){
console.log(data);
});
/*************************************
callback ... 执行了 1 次
*************************************/
如果用Promise改写,会是什么样的效果呢?
'use strict';
// 定义一个计数器,用来统计回调函数执行的次数
let count = 1;
/**
* 定义一个异步执行函数,返回值是一个Promise对象
*/
function asyncFunc() {
let promise = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(`resolve ... 执行了 ${count} 次`);
count++;
}, 1000);
});
return promise;
};
// 调用函数
asyncFunc().then(function(data){
console.log(data);
});
/*************************************
resolve ... 执行了 1 次
*************************************/
怎么感觉代码更复杂,更加难于理解了?确实,在定义函数的时候,需要返回一个Promise对象,增加了代码量,看着没什么优势。从这点来看,学这个东西,完全没有必要啊。
别着急,接着往下走。。。。
回调函数执行多次
上面的情况只是调用一次函数,那么调用多次呢?比如调用个五次、七次。下面咱们看看调用七次的情况。
首先,还是先看看使用回调函数的情况:
'use strict';
// 定义一个计数器,用来统计回调函数执行的次数
let count = 1;
/**
* 定义一个异步执行函数,参数是回调函数
*/
function asyncFunc(callback) {
setTimeout(function () {
callback(`callback ... 执行了 ${count} 次`);
count++;
}, 1000);
};
// 调用函数
asyncFunc(function (data) { // 第一次调用
console.log(data);
asyncFunc(function (data) { // 第二次调用
console.log(data);
asyncFunc(function (data) { // 第三次调用
console.log(data);
asyncFunc(function (data) { // 第四次调用
console.log(data);
asyncFunc(function (data) { // 第五次调用
console.log(data);
asyncFunc(function (data) { // 第六次调用
console.log(data);
asyncFunc(function (data) { // 第七次调用
console.log(data);
});
});
});
});
});
});
});
/*************************************
callback ... 执行了 1 次
callback ... 执行了 2 次
callback ... 执行了 3 次
callback ... 执行了 4 次
callback ... 执行了 5 次
callback ... 执行了 6 次
callback ... 执行了 7 次
*************************************/
看着挺好看,但是,当回调更多的时候,如何分清是哪个回调,又怎么判断哪个括号对应哪句代码呢?其实这就是一个回调地狱,这样的代码可读性差。
这个时候,Promise就可以发挥作用了。
看了回调函数,再来看看使用Promise的情况:
'use strict';
// 定义一个计数器,用来统计回调函数执行的次数
let count = 1;
/**
* 定义一个异步执行函数,返回值是一个Promise对象
*/
function asyncFunc() {
let promise = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(`resolve ... 执行了 ${count} 次`);
count++;
}, 1000);
});
return promise;
};
// 调用函数
asyncFunc() // 第一次调用
.then(function (data) {
console.log(data);
return asyncFunc(); // 第二次调用
})
.then(function (data) {
console.log(data);
return asyncFunc(); // 第三次调用
})
.then(function (data) {
console.log(data);
return asyncFunc(); // 第四次调用
})
.then(function (data) {
console.log(data);
return asyncFunc(); // 第五次调用
})
.then(function (data) {
console.log(data);
return asyncFunc(); // 第六次调用
})
.then(function (data) {
console.log(data);
return asyncFunc(); // 第七次调用
})
.then(function (data) {
console.log(data);
});
/*************************************
resolve ... 执行了 1 次
resolve ... 执行了 2 次
resolve ... 执行了 3 次
resolve ... 执行了 4 次
resolve ... 执行了 5 次
resolve ... 执行了 6 次
resolve ... 执行了 7 次
*************************************/
会发现,是用了Promise代码可读性变得很好,以后也便于修改。
用Promise包装旧回调写法
现在可以看出Promise的好处,如果想以后都使用Promise,可否实现?
肯定可以实现啊,下面就使用Promise来包装上面的回调函数。
使用Promise包裹的时候,只需要经过下面几个步骤:
- 定义一个函数,返回Promise对象
- 在Promise对象中调用异步执行函数,参数是创建Promise对象时传递的函数
'use strict';
// 定义一个计数器,用来统计回调函数执行的次数
let count = 1;
/**
* 定义一个异步执行函数,参数是回调函数
*/
function asyncFunc(callback) {
setTimeout(function () {
callback(`callback ... 执行了 ${count} 次`);
count++;
}, 1000);
};
/**
* 包装异步执行函数,返回一个Promise对象
*/
function wrapperAsyncFunc() {
let promise = new Promise(function (resolve, reject) {
asyncFunc(resolve);
});
return promise;
};
// 调用
wrapperAsyncFunc()
.then(function (data) {
console.log(data);
return wrapperAsyncFunc();
})
.then(function (data) {
console.log(data);
});
/*************************************
callback ... 执行了 1 次
callback ... 执行了 2 次
*************************************/
认识Promise
在谷歌浏览器中,我们看看Promise都包含什么:
方法概述
可以看到,在Promise的prototype上有三个方法,也就是实例对象上有三个方法:
new Promise(function(resolve, reject) { ... } );
promise.then(onFulfilled[, onRejected]);
promise.catch(onRejected);
Promise对象本身的方法,就是静态方法:
Promise.all(iterable);
Promise.race(iterable);
Promise.reject(reason);
Promise.resolve();
Promise的三种状态
在认识这些方法之前,先认识一下Promise的三种状态:
- pending: 初始状态,创建Promise成功后的状态
- fulfilled: 操作执行成功后的状态
- rejected: 操作执行失败后的状态
Promise的实例方法
首先,先来创建一个Promise对象,可以根据num的值,来调节执行的函数是resolve还是reject:
new Promise(function(resolve, reject) { ... } );
'use strict';
let num = 3;
// 创建一个Promise对象
let promise = new Promise(function (resolve, reject) {
if (num > 5) {
resolve('success ...'); // 操作执行成功执行的函数
} else {
reject('failure ...'); // 操作执行失败执行的函数
}
});
首先明确一点,Promise对象创建的时候,立即执行。可是,既然是立即执行,怎么获取对应的状态值呢?下面就要使用then方法了。
promise.then(onFulfilled[, onRejected]);
promise.then(function (data) {
console.log(data);
}, function (reason) {
console.log(reason);
});
上面的代码中,在then方法中需要传递两个回调函数,这样看着会有点乱。有没有更优的解决方式?有,这个时候要使用catch方法。
// 把上面的代码进行简化
promise
.then(function (data) { // 状态变为fulfilled后执行的回调
console.log(data);
}).catch(function (reason) { // 状态变为rejected后执行的回调
console.log(reason);
});
Promise的静态方法
为了方法认识Promise.all和Promise.race,定义三个Promise对象:
let promise1 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('promise1 ...');
console.log('done1 ...');
}, 2000); // 延迟2秒
});
let promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('promise2 ...');
console.log('done2 ...');
}, 4000); // 延迟4秒
});
let promise3 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('promise3 ...');
console.log('done3 ...');
}, 6000); // 延迟6秒
});
Promise.all
Promise.all的作用是在参数中的所有Promise对象完全执行完成的时候,才会执行自身的then或catch方法:
Promise
.all([promise1, promise2, promise3])
.then(function (data) {
console.log(data);
})
.catch(function (reason) {
console.log(reason);
});
/***************************************
done1 ...
done2 ...
done3 ...
[ 'promise1 ...', 'promise2 ...', 'promise3 ...' ]
***************************************/
Promise.all中所有的Promise对象状态都变为fulfiled状态时,才会触发then方法;其中一个变为rejected状态,那么就触发catch方法。
需要注意的是,即使触发了catch方法,其他的Promise对象中的代码还是会正常执行的。因为这是Promise的特性,创建之后,立即执行。
更改一个Promise对象之后,结果就会成下面的状态:
let promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
reject('promise2 ...');
console.log('done2 ...');
}, 4000);
});
/***************************************
done1 ...
done2 ...
promise2 ...
done3 ...
***************************************/
Promise.race
Promise.race的作用是在参数中的Promise对象中的一个执行完成的时候,就会执行自身的then或catch方法:
Promise
.race([promise1, promise2, promise3])
.then(function (data) {
console.log(data);
})
.catch(function (reason) {
console.log(reason);
});
/***************************************
done1 ...
promise1 ...
done2 ...
done3 ...
***************************************/
需要注意的是:Promise.all方法是所有的都执行完成才会触发then方法,就是不落下任何一个人;而Promise.race方法是有一个执行完成就会触发then方法,就是看谁跑得快。
后续的两个方法,以后用到的时候再补充,因为这些内容对现在而言已经够用了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。