做为一个入门级前端,今天是一个很是值得记念的日子,由于这是我第一次在论坛上发表帖子,做为起步。虽然我以为本身水平仍是十分的有限,对一些细节的理解还不是很透彻,可是仍是要迈出这一步,不论是给别的新手做为学习参考,仍是本身之后回顾,总以为须要把本身的成长记录下来,但愿本身之后仍是要多坚持,若是有不对的地方仍是但愿你们及时提出来,共同进步前端
今天有时间翻到了es6的promise,可能你们都对此熟悉不过,我以前一直以为promise也很简单,可是今天确实让我对promise有了一个新的了解,之前的理解多是错误的。。。先来看看官方的promise的定义是:es6
所谓Promise,简单说就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。从语法上说,Promise 是一个对象,从它能够获取异步操做的消息。Promise 提供统一的 API,各类异步操做均可以用一样的方法进行处理。数组
特色:promise
(1)对象的状态不受外界影响。Promise对象表明一个异步操做,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操做的结果,能够决定当前是哪种状态,任何其余操做都没法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其余手段没法改变bash
(2)一旦状态改变,就不会再变,任什么时候候均可以获得这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种状况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。若是改变已经发生了,你再对Promise对象添加回调函数,也会当即获得这个结果。这与事件(Event)彻底不一样,事件的特色是,若是你错过了它,再去监听,是得不到结果的。异步
初读上面这段话我读了不下5次,可是我仍是没能真正了解其真正表达的意思,因而我查阅了部分资料,终于找到了一个比较容易理解的说法,这个估计小白应该是能够看得懂得。编辑器
就拿作饭吃饭洗碗来举例子吧,这是三个步骤,第一步作饭,再第二步吃饭的时候咱们须要拿到第一步作的饭,在第三步洗碗的时候咱们须要拿到第二步的碗筷,并且这三个步骤必须是按照顺序执行,有严格的前后顺序。函数
//作饭
function cook(){
console.log('开始作饭。');
var p = new Promise(function(resolve, reject){ //作一些异步操做
setTimeout(function(){
console.log('作饭完毕!');
resolve('鸡蛋炒饭');
}, 1000);
});
return p;
}
//吃饭
function eat(data){
console.log('开始吃饭:' + data);
var p = new Promise(function(resolve, reject){ //作一些异步操做
setTimeout(function(){
console.log('吃饭完毕!');
resolve('一个碗和一双筷子');
}, 2000);
});
return p;
}
//洗碗
function wash(data){
console.log('开始洗碗:' + data);
var p = new Promise(function(resolve, reject){ //作一些异步操做
setTimeout(function(){
console.log('洗碗完毕!');
resolve('干净的碗筷');
}, 2000);
});
return p;
}
//函数调用
cook().then(res1 => {
console.log(res1,'这是第一步传递给第二步的参数')
return eat(res1)
}).then(res2 => {
console.log(res2,'这是第二步传给第三步的参数')
return wash(res2)
}).then(res3 => {
console.log(res3,'饭吃完了还你干净的碗筷')
})
复制代码
结果以下: 学习
看完上面的代码你们可能会有好多疑问,我在这里把我当时初学promise的疑问和你们分享一下:ui
答:Promise也有一些缺点。首先,没法取消Promise,一旦新建它就会当即执行,没法中途取消。其次,若是不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,没法得知目前进展到哪个阶段(刚刚开始仍是即将完成)。
这是promise的一些缺点,一旦新建它就会当即执行 因此为了控制这个promise对象何时执行,在开发过程当中咱们须要在外面包裹一个函数,经过调用函数的形式来控制promise的执行。你们能够在本身的编辑器中试试。
new Promise(function(resolve, reject) {
setTimeout(()=> {
console.log('开车!!!')
},2000)
});
复制代码
答:由于有了Promise对象,就能够将异步操做以同步操做的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操做更加容易。
//函数调用
cook().then(res1 => {
console.log(res1,'这是第一步传递给第二步的参数')
return eat(res1)
}).then(res2 => {
console.log(res2,'这是第二步传给第三步的参数')
return wash(res2)
}).then(res3 => {
console.log(res3,'饭吃完了还你干净的碗筷')
})
复制代码
你们看个人这段代码 在执行了cook()函数的时候cook函数return出来一个promise对象,promise对象上有.then()或.catch()方法,那么直接cook().then就能够在.then()方法中咱们能够拿到promise对象中向外传递的参数,这个参数咱们将传递给下一个eat()函数,做为eat()函数的参数继续执行。 而eat()函数也return了一个promise对象。那么咱们将咱们的这段代码拆解一下:
第一步: cook() //拿到的是cook return出来的promise对象
第二步: cook().then(res => {
console.log(res) //拿到内部向外部传递的参数
})
第三步: cook().then(res => {
console.log(res)
return eat(res) //eat执行之后return的是eat的promise对象,而后再把这个对象继续向外return
}) //那么第三步的最终结果就是eat()的promise对象
第四步: cook().then(res => {
console.log(res)
return eat(res) //此时的结果是eat()的promise对象
}).then(res2 => {
// 此时eat的promise又有.then方法 .....以此类推
})
复制代码
咱们就这样一步一步的完成了整个作饭、吃饭、洗碗的整个流程。 纵观以上代码你会发现虽然每个操做流程中我都是以异步函数setTimeout来进行的,可是在调用过程当中确是按照 作饭-吃饭-洗碗的正常流程表达的。这不是promise的有了Promise对象,就能够将异步操做以同步操做的流程表达出来,避免了层层嵌套的回调函数
//作饭
function cook(){
console.log('开始作饭。');
var p = new Promise(function(resolve, reject){ //作一些异步操做
setTimeout(function(){
console.log('饭糊了....无法吃');
reject('糊了的饭');
}, 1000);
});
return p;
}
//吃饭
function eat(data){
console.log('开始吃饭:' + data);
var p = new Promise(function(resolve, reject){ //作一些异步操做
setTimeout(function(){
console.log('吃饭完毕!');
resolve('一个碗和一双筷子');
}, 2000);
});
return p;
}
cook().then(res1 => {
console.log(res1,'这是第一步传递给第二步的参数')
return eat(res1)
}).catch(err => {
console.log(err,'返回错误')
})
复制代码
catch()方法用来指定 reject 的回调。
function tackBus1(){
var p = new Promise(function(resolve, reject){
//作一些异步操做
setTimeout(function(){
console.log('甲正在上车');
resolve('甲坐好了');
}, 1000);
});
return p;
}
function tackBus2(){
var p = new Promise(function(resolve, reject){
//作一些异步操做
setTimeout(function(){
console.log('乙正在上车');
resolve('乙坐好了');
}, 2000);
});
return p;
}
function tackBus3(){
var p = new Promise(function(resolve, reject){
//作一些异步操做
setTimeout(function(){
console.log('丙正在上车');
resolve('丙坐好了');
}, 3000);
});
return p;
}
Promise.all([tackBus1(),tackBus2(),tackBus3()]).then(res => {
console.log(res)
})
复制代码
司机在等人上车,在乘客都坐稳了之后开车发车,这就用Promise.all来执行,all接收一个数组参数,里面的值最终都算返回Promise对象。这样,三个异步操做的并行执行的,等到它们都执行完后才会进到then里面。那么,三个异步操做返回的数据哪里去了呢?都在then里面呢,all会把全部异步操做的结果放进一个数组中传给then,就是上面的results。因此上面代码的输出结果就是:
这是.all的方法,promise还有一种.race的方法,它和all方法的区别就是: all方法的效果其实是谁跑的慢,以谁为准执行回调,那么相对的就有另外一个方法谁跑的快,以谁为准执行回调,刚刚的执行结果我设置了他们的时间间隔分别是1s,2s,3s,在等最慢的执行完之后才执行了all这个回调方法,如今我们来试试promise.race方法
function tackBus1(){
var p = new Promise(function(resolve, reject){
//作一些异步操做
setTimeout(function(){
console.log('甲正在上车');
resolve('甲坐好了');
}, 1000);
});
return p;
}
function tackBus2(){
var p = new Promise(function(resolve, reject){
//作一些异步操做
setTimeout(function(){
console.log('乙正在上车');
resolve('乙坐好了');
}, 2000);
});
return p;
}
function tackBus3(){
var p = new Promise(function(resolve, reject){
//作一些异步操做
setTimeout(function(){
console.log('丙正在上车');
resolve('丙坐好了');
}, 3000);
});
return p;
}
Promise.race([tackBus1(),tackBus2(),tackBus3()]).then(res => {
console.log(res)
})
复制代码
结果以下:
在看到promise的时候有一个地方仍是令我有困惑,如今先留一个悬念,你们能够先看看下面这段代码,你以为输出结果是什么呢? 咱们下回见!
setTimeout(function(){
console.log('1')
});
new Promise(function(resolve){
console.log('2');
resolve();
}).then(function(){
console.log('3')
});
console.log('4');
复制代码