1.为什么要使用promise
/*
需求:异步的按顺序去读取1.txt、2.txt、3.txt文件的内容
假设 1.txt内容为111 、2.txt内容为222、3.txt内容为333
*/
var fs = require('fs');、
fs.readFile('./files/1.txt','utf8',function(err,data){
if(err){
throw err;
}
console.log(data);
})
fs.readFile('./files/1.txt','utf8',function(err,data){
if(err){
throw err;
}
console.log(data);
})
fs.readFile('./files/1.txt','utf8',function(err,data){
if(err){
throw err;
}
console.log(data);
})
/*
结果:111 222 333。(好像满足需求啊)
*/
/*
问题:上面的代码虽然正常按照顺序读取了文件,因为文件的内容都非常少,io读取耗时少,但当2.txt内容较多时候,2.txt的内容是最后输出的。因为他们都是异步操作,不知道谁先读取完。 这得取决于异步任务IO的耗时。
*/
/*
常规解决办法:
在第一个异步任务读取成功之后再读取第二个异步任务,
第二成功后,在读取第三个异步任务
*/
//读取第一个异步任务
fs.readFile('./files/1.txt','utf8',function(err,data){
if(err){
throw err;
}
console.log(data);
//读取第二个异步任务
fs.readFile('./files/2.txt','utf8',function(err,data){
console.log(data);
//读取第三个异步任务
fs.readFile('./files/3.txt','utf8',function(err,data){
console.log(data);
})
})
})
/*
结果: 111 222 333 (这必须按照顺序输出的,结果杠杆的)
*/
/*
问题:
以上按照顺序执行多个异步任务产生的问题:`回调地狱`问题(层层包裹进行回调,代码也不够优雅)
*/
/*
解决办法:采用es6,提供的promise来解决上述产生的问题。
*/
2.Promise基本使用
promise介绍:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
//简单说promise就是用来执行异步任务的,可以解决上面所说的回调地狱问题,
//语法:new Promise(function(callback){})
var fs = require('fs');
var promise = new Promise(function(resolve,reject){
//这里就是写异步的代码,只要new Promise操作,就会立刻执行这里的代码
//两个参数 resolve 异步执行成功的回调函数,reject异步执行失败的回调函数
fs.readFile('./files/1.txt', 'utf8', function (err, data) {
if (err) {
throw err;
}
console.log(data);
})
});
//运行结果:111
3.promise参数resolve和reject
两个参数
- resolve 异步执行成功的回调函数,
- reject异步执行失败的回调函数
var fs = require('fs');
var promise = new Promise(function (resolve, reject) {
//两个参数: resolve 成功的回调函数名 , reject失败的回调函数名
fs.readFile('./files/111.txt', 'utf8', function (err, data) {
if (err) {
//说明失败了,要执行失败的回调函数
reject(err);
} else {
//成功的逻辑
resolve(data);
}
//等价于
// err ? reject(err) : resolve(data);
})
});
//new Promise返回的是一个promise对象,
//这个对象有一个方法叫做then,在其原型对象上
//通过这then方法可以指定成功和失败的回调函数
//语法:promise.then(successCallback,errorCallback);
promise.then(function (data) {
//then第一个函数是成功的回调,参数是resolve(err)中的data
console.log('成功:' + data); // 若成功,运行结果:成功:111
}, function (err) {
//then第二个函数是失败的回调函数,参数是reject(err)中的err错误对象
console.log('失败:' + err);
});
4.解决回调地狱问题
//封装一个异步读取文件的内容的函数
//此函数返回对应异步任务的promise对象
function getFileByPath(path) {
return new Promise(function (resolve,reject) {
fs.readFile(path, 'utf8', function (err, data) {
if(err){
reject(err); //失败
}else{
resolve(data); //成功
}
})
});
}
//由于then通过getFileByPath返回的是一个promise对象,所以可以继续.then串联调用(链式调用)
getFileByPath('./files/1.txt')
.then(function(data){
console.log("成功:"+data);
return getFileByPath('./files/2.txt');
},function(err){
console.log("失败:"+err);
return getFileByPath('./files/2.txt');
})
.then(function(data){
console.log("成功:"+data);
return getFileByPath('./files/3.txt');
},function(err){
console.log("失败:"+err);
return getFileByPath('./files/3.txt');
})
.then(function(data){
console.log("成功:"+data);
},function(err){
console.log("失败:"+err);
});
注意:上面一定会保证按照.then的顺序去执行异步的代码,
如果某个异步任务有错误则会触发对应then的第二个错误的回调函数。即每个promise对象都有对应的错误回调,对其他的promise不影响。毕竟promise的英文翻译就是保证。
5.catch的使用
catch也是在Promise.prototype原型上定义的。
image.png
需求:如果多个promise任务,其中有一个失败了,则终止后面的所有的promise执行
getFileByPath('./files/1.txt')
.then(function(data){
console.log("成功:"+data);
return getFileByPath('./files/2.txt');
}) //上面的then通过getFileByPath返回的是一个promise对象,所以可以继续.then串联调用(链式调用)
.then(function(data){
console.log("成功:"+data);
return getFileByPath('./files/3.txt');
})
.then(function(data){
console.log("成功:"+data);
})
.catch(function(err){
// catch作用: 上面所有的promise如果其中一个有错误,
//则终止下面所有的promise执行,且直接进入到catch中获取对应promise的异常错误信息
console.log('catch:'+err);
})
注意:如果在then中定义了错误回调则不会进入到上面的catch中,这是因为promise对象指定了对应的错误处理回调。
6 then-catch-finally
var fs = require('fs');
var promise = new Promise(function (resolve, reject) {
//两个参数: resolve 成功的回调函数名 , reject失败的回调函数名
fs.readFile('./files/11.txt', 'utf8', function (err, data) {
if (err) {
//说明失败了,要执行失败的回调函数
reject(err);
} else {
//成功的逻辑
resolve(data);
}
})
});
//new Promise返回的是一个promise对象,
//这个对象有一个方法叫做then,在其原型对象上
//通过这then方法可以指定成功和失败的回调函数
//语法:promise.then(successCallback,errorCallback);
promise.then(function (data) {
//then第一个函数是成功的回调,参数是resolve(err)中的data
console.log('成功:' + data); // 若成功,运行结果:成功:111
}).catch(function(err){
//then第二参数错误回调换成这里catch也行,两者选其一
console.log('err');
}).finally(function(){
//无论失败成功都会执行
console.log('完成');
})
7. Promise.all静态方法的使用
参照:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise#%E6%96%B9%E6%B3%95
需求:把所有的promise执行成功的结果保存在一个数组中。。
var promise1 = getFileByPath('./files/1.txt');
var promise2 = getFileByPath('./files/2.txt');
var promise3 = getFileByPath('./files/3.txt');
//执行多个异步任务,
Promise.all([promise3,promise1,promise2]).then(function(data){
console.log(data);
},function(err){
console.log('错误了:'+err);
})
//Promise.all尤其是在一个页面上发起多个ajax请求的时候,如果要同时保证他们成功,则使用Promise.all是最合适不过的了。其中一个失败则也可以在then的第二个回调做失败的逻辑。
若都成功,运行结果:['333','111','22']
注意:Promise.all的成功结果是返回一个数组,且数组中数据的结果顺序与Promise.all数组的传参顺序是一样的。