浅谈promise和js执行机制(一)

做为一个入门级前端,今天是一个很是值得记念的日子,由于这是我第一次在论坛上发表帖子,做为起步。虽然我以为本身水平仍是十分的有限,对一些细节的理解还不是很透彻,可是仍是要迈出这一步,不论是给别的新手做为学习参考,仍是本身之后回顾,总以为须要把本身的成长记录下来,但愿本身之后仍是要多坚持,若是有不对的地方仍是但愿你们及时提出来,共同进步前端

今天有时间翻到了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

(1)为何我要在promise对象外面须要用一个函数来包裹起来呢?

答:Promise也有一些缺点。首先,没法取消Promise,一旦新建它就会当即执行,没法中途取消。其次,若是不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,没法得知目前进展到哪个阶段(刚刚开始仍是即将完成)。

这是promise的一些缺点,一旦新建它就会当即执行 因此为了控制这个promise对象何时执行,在开发过程当中咱们须要在外面包裹一个函数,经过调用函数的形式来控制promise的执行。你们能够在本身的编辑器中试试。

new Promise(function(resolve, reject) {
	setTimeout(()=> {
		console.log('开车!!!')
	},2000)
});
复制代码

(2)在每个函数中我为何要将个人promise对象用一个变量接收而后return出去呢?

答:由于有了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对象,就能够将异步操做以同步操做的流程表达出来,避免了层层嵌套的回调函数

(3)如何判断失败的状况呢?

//作饭
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 的回调。

(4)如何在多个异步操做都完成后才执行回调呢?

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)
})
复制代码

结果以下:

能够看出来在甲执行完毕后当即就执行了.race()方法,可是不耽误其余两个异步操做的进行,.race()中拿到的参数也只是当前最早执行完的异步操做中传递出来的参数。

(4)由promise联系到的js的宏任务和微任务

在看到promise的时候有一个地方仍是令我有困惑,如今先留一个悬念,你们能够先看看下面这段代码,你以为输出结果是什么呢? 咱们下回见!

setTimeout(function(){
  console.log('1')
});
new Promise(function(resolve){
    console.log('2');
    resolve();
}).then(function(){
    console.log('3')
});
console.log('4');
复制代码
相关文章
相关标签/搜索