promise 有哪些优缺点?
优点:
1.可以解决异步嵌套的问题; 2.可以解决多个异步并发问题
缺点
1.promise 基于回调的(要不停的写函数) 2.promise 无法终止异步
promise 是承诺的意思,promise是一个类,这个类上有3个状态:等待态(默认)pending,成功态fulfilled,失败态rejected;一旦成功了就不能失败,失败了也不能成功
promise 特点:
1).promise是一个类, 这个类上有3个状态:等待态(默认),成功态,失败态;一旦成功了就不能失败,失败了也不能成功 resolve 代表成功; reject 代表失败
2). 每个promise 实例 都有一个then方法,then方法里有2个参数(方法):成功后走的逻辑 和 失败后走的逻辑;
3).如果new Promise的时候 报错了, 会变成失败态(抛错也算失败)
1.promise 简单用法
let promise = new Promise( (resolve , reject) => { //Promise 是一个类,参数传一个函数,这个函数叫做 executor 执行器,执行器会立刻执行
// 执行器会带有2个参数:resolve -成功 ; reject - 失败
console.log(1);
// throw new Error('失败'); //throw 语句抛出一个错误。用于:创建自定义错误
reject(222)
// resolve('nihao')
}).then( data => { //每个promise 实例 都有一个then方法,then方法里有2个参数(方法):成功后走的逻辑 和 失败后走的逻辑;
//成功走这部分
console.log(data);
}, err => {
//失败走这部分
console.log(err,'111');
})
2.Promise 实现原理
// 因为状态常用,所以放在常量里
// 这里面的方法分为:实例上的方法,私有方法 和原型上的方法
// 一般写类 就是面向对象,写函数就是面向过程
const PENDING = 'PENDIND';
const RESOLVED = 'RESOLVED';
const REJECTED = 'REJECTED';
class Promise{
constructor(executor){ //new Promise的时候会传一个函数,这个函数就是executor,执行器,
//new Promise 就是用构造函数 或者constructor 构造实例
this.state = PENDING ; //默认是等待态, 在构造器里的属性,每个函数都有自己的属性(每个实例的值不同-独立),且可以在原型的方法上使用
this.value = undefined; //成功的值
this.reason = undefined; //失败的原因
this.onResolvedCallbaks = []; //成功的回调函数的数组
this.onRejectedCallbacks = []; //失败的回调函数的数组
// 构造函数executor里有两个函数参数,这两个函数不需要在then方法里拿到,且属于当前构造函数里,所以在构造器里定义
//这里 resolve 和 reject方法就是私有方法,在类外面找不到这个方法,不能通过事件调用
let resolve = (value) => { //这里用箭头函数 可以避免this 指向问题,不然以后的 resolve()里的this 可能指向window
//成功函数,resolve 的时候要传一个参数:成功的值,但是这个参数我们要在then 方法里使用
// 所以我们要把这个参数也存在构造函数/构造器里
if(this.state === PENDING){ //状态为PENDING才能改指 改状态
this.value = value; //一调用resolve方法酒吧 调用时传的参数 存给构造函数构造的实例里了
this.state = RESOLVED; //成功后修改状态
this.onResolvedCallbaks.forEach( fn => fn()); //执行then时存起来的方法,在执行器走完后执行
}
}
let reject = (reason) => {
//失败函数,reject的时候要传一个参数:失败的原因,要在then 方法里使用这个参数
if(this.state === PENDING){
this.reason = reason ;
this.state = REJECTED; //失败后修改状态
this.onRejectedCallbacks.forEach( fn => fn());
}
}
try{
executor(resolve , reject ) ; //执行器会立刻执行,一new 就执行了,我们把成功函数 和 失败函数当作参数 传给执行器
// 这是构造函数里
}catch(e){
reject(e); //如果执行 executor方法时报错了,就执行reject方法;
}
}
// 1.看这个属性能否在原型上使用
// 2.看属性是否公用
// then 里面要用到状态, 且每个实例要有自己的状态,不能放在公用上,所以就把状态放在自己的构造函数中constructor里
then(onfulfilled,onrejected){ // then方法有2个 方法参数
// 同步
// then方法每个实例都有,肯定是在原型上的,也就是Promise.prototype.then = function(){}
if(this.state === RESOLVED){ //成功的时候执行第一个参数
onfulfilled(this.value); //执行第一个方法参数,传一个参数,参数是在new promise是执行器立即行,执行成功后传的值
}
if(this.state ===REJECTED){
onrejected(this.reason)
}
// 异步情况
// 如果是异步的 就先订阅好
if(this.state === PENDING){ //当执行then()但状态不是成功也不是失败,是等待说明还没执行完,可以先把方法存到数组里
//等执行完再调用这些方法
// this.onResolvedCallbaks.push(onfulfilled); 这样写可以,但是没办法在里面添加一些自己的方法了
this.onResolvedCallbaks.push( () => { //我们可以这样写返回一个函数,在这个函数里调用执行 方法,还可以加自己的逻辑
// todo...
onfulfilled(this.value);
})
this.onRejectedCallbacks.push(() => { //then 的时候 new Promise()没有执行完,把方法存到数组里,等执行完在执行,
// 应该是执行器里的resolve 和 reject方法里执行这些方法
// todo...
onrejected(this.reason);
});
}
}
}
module.exports = Promise;
3. promise里then的‘发布订阅’
promise 还有个发布订阅的功能:一个promise 可以then()多次,如果new的时候 执行器里面 失败了,则每个then方法里都走onredected 方法
promise里时异步的调用resolve 和reject的。就是当走到then的收,new Promise()实际还每走完,此时promise状态不是成功态 也不是失败态 而是等待态,
此时我们就把多个then方法里,成功要执行和函数参数 和 失败要执行的函数参数 分别存到一起,等注册完 再执行这个方法 。这个过程就和之前说的发布订阅 很类似,这个实现原理,再第2小结里有写
let promise = new Promise( (resolve , reject) => { //Promise 执行器
// throw new Error('失败'); //
setTimeout(()=>{ //这里是异步,可能走到下面then的时候,这里还没有走完,没有状态
reject(333)
},100)
})
promise.then( data => {
//成功走这部分
console.log(data);
}, err => {
//失败走这部分
console.log(err,'111');
});
promise.then( data => {
//成功走这部分
console.log(data);
}, err => {
//失败走这部分
console.log(err,'111');
});
promise.then( data => {
//成功走这部分
console.log(data);
}, err => {
//失败走这部分
console.log(err,'111');
});
4. promise里then的用法
目标:要分段读取, 第一个文件的输出,是下一个文件的输入
1.小白写法:
如下写法的缺点:1.回调嵌套问题,不好维护 2. 如果有err 要在每个函数里嵌套一层:异步错误处理问题,不能统一
let fs = require('fs');
fs.readFile('./name.txt','utf8',function(err,data){
fs.readFile(data,'utf8',function(err,data){ //第一个参数写data, 是上一个文件的输出
console.log(data);
})
})
2.我们可以写一个方法 把它变成promise
let fs = require('fs');
function read(url) {
return new Promise((resolve, reject) => { //resolve , reject 这是两个回调,执行器执行完调用其中之一,并传参数:值或原因
fs.readFile(url, 'utf8', function (err, data) { //第一个参数写data, 是上一个文件的输出
if (err) reject(err); //如果失败了就调用 方法reject()
resolve(data); //否则成功了调用resolve()
})
})
}
// new Promise() 构造一个实例对象,里面的执行器是new 时立即执行,然后根据成功或失败,调用resolve /reject方法改变实例对象的状态,和值或原因
read('./name.txt').then( data => { //这里 read() 被我们写成了一个promise 实例 ,它有then方法
read(data ).then( data => { // name.txt 文件里放发是'./age.txt',读取后返回的是我们需要的
console.log(data);
},err => {
console.log(err);
})
},err => {
console.log(err);
})
如果一个promise的then方法中的函数(成功或失败)返回的结果是一个promise的话,
会自动将这个promise执行,并且采用它的状态,如果成功了 会将成功的结果向外层的下一个then 传递也就是说:里面的结果会决定走外层的then方法里的成功 或 失败
3.返回的promise 是成功态
read('./name.txt').then( data => {
return read(data); //这里返回的是一个promise,会自动将这个promise执行,要返回,要有return
// 1.并采用它的状态,如果成功了会将成功的结果向外层的 下一个then 传递
},err => {
console.log(err);
}).then( data => { //这里 read() 被我们写成了一个promise 实例 ,它有then方法
console.log(data +'222'); //2.这里的data是上一个then 返回的数据值
},err => {
console.log(err);
})
上面代码中:第一个then里返回一个promise,这个promise 成功了,所以这个promise外层的then,也就是第二个then 也走成功逻辑的代码;
4.返回的promise是失败态
read('./name.txt').then(data => {
return read(data + '111'); //1.这个返回的promise 会走reject,那下一个then 就走onrejected
console.log(err);
}).then(data => {
console.log(data + '222');
}, err => {
console.log(err); //2. 走这里
return undefined; //3. 如果失败了,走error,返回的是一个普通值,那么会将这个普通值作为下一次的成功结果
// 或者false等普通值,只要不是promise都可以
}).then(data => { // 4. 上一个then 失败是返回的undefined,这次就走成功
console.log(data + '222'); //这里的data是上次返回的undefined
// 5. 我们希望走完这个then 就不要再走以后的then了,就放一个空的promise,
// 放值 或者 抛错都会走下面的成功或失败,就只能放promise
return new Promise( ()=>{}) //5. 终止promise,可以返回一个pending的promise
}, err => {
console.log(err);
}).then(data => { //6. 这个then 不会执行了
console.log(111111111);
}, err => {
console.log(2222222);
})
总结:
1.只有两种情况会失败:返回一个失败的promise(就是promise的执行器里掉的reject方法参数), 或者 抛出异常错误;
2.每次执行promise实例.then()的时候都会返回一个新的promise,这样才可以一直调用.then(),而且是新的,不然返回的同一个promise 成功就不能失败了,失败就不能成功了
3.then()里面的onrejected 可以不写,然后写一个.catch(err =>{}) 到时候有过自己有onrejected 就走自己的onrejected,如果没有就走 .catch()