从上⾯的案例介绍得知Promise的作⽤是解决“回调地狱”,他的解决⽅式是将回调嵌套拆成链式调⽤,这样便可以按照上下顺序来进⾏异步代码的流程控制。那么Promise是如何实现这个能⼒的呢?Promise的结构Promise对象是⼀个JavaScript对象,在⽀持ES6语法的运⾏环境中作为全局对象提供,他的初始化⽅式如下:关于回调函数这⾥涉及到⼀个概念:JavaScript语⾔中,有⼀个特殊的函数叫做回调函数。回调函数的特点是把函数作为变量看待,由于JavaScript变量可以作为函数的形参并且函数可以通过声明变量的⽅式匿名创建,所以我们可以在定义函数时将⼀个函数的参数当作函数来执⾏,进⽽在调⽤时在参数的位置编写⼀个执⾏函数,代码如下:上⾯的代码结构,就是JavaScript中典型的回调函数结构。按照我们在事件循环中介绍的JavaScript函数运⾏机制,会发现其实回调函数本身是同步代码,这是⼀个需要【重点理解】的知识点。通常在编写JavaScript代码时,使⽤的回调嵌套的形式⼤多是异步函数,所以⼀些开发者可能会下意识的认为,凡是回调形式的函数都是异步流程。其实并不是这样的,真正的解释是(敲⿊板):JavaScript中的回调函数结构,默认是同步的结构,由于JavaScript单线程异步模型的规则,如果想要编写异步的代码,必须使⽤回调嵌套的形式才能实现,所以回调函数结构不⼀定是异步代码,但是异步代码⼀定是回调函数结构。 },1000) })}).then(function(){ //第三秒后执⾏的逻辑 console.log('第三秒之后发⽣的事情')})//fn:是初始化过程中调⽤的函数他是同步的回调函数var p = new Promise(fn)//把fn当作函数对象那么就可以在test函数中使⽤()执⾏他function test(fn){ fn()}//那么运⾏test的时候fn也会随着执⾏,所以test中传⼊的匿名函数就会运⾏test(function(){ ...})那么为什么异步流程都需要回调函数?依然举个栗⼦:分析下列代码的输出顺序(先不要看答案):分析下列代码的输出顺序(先不要看答案):再思考⼀下,如果我们有⼀个变量a的值为0,想要1秒之后设置他的值为1,并且我们想要在之后得到a的新结果,这个逻辑中如果1秒之后设置a为1采⽤的是setTimeout,那么我们在同步结构⾥能否实现?先参考下⾯的案例:解析上述代码块输出的结果⼀定是0,由于JavaScript单线程异步模型的知识,我们可以得知,当前的代码块中setTimeout的回调函数是⼀个宏任务,会在本次的同步代码执⾏完毕后执⾏,所以声明a=0和输出a的值这两⾏代码会优先执⾏,这时对a设置1的事件还没有发⽣,所以输出的结果就⼀定为0。接下来对代码做如下改造,我们试图使⽤阻塞的⽅式来获取异步代码的结果:function test(fn){ fn()}console.log(1)test(function(){ console.log(2)})console.log(3)//这段代码的输出顺序应该是*、*、*,因为他属于直接进⼊执⾏栈的程序,会按照正常程序解析的流程输出function test(fn){ setTimeout(fn,0)}console.log(1)test(function(){ console.log(2)})console.log(3)//这段代码会输出*、*、*,因为在调⽤test的时候settimeout将fn放到了异步任务队列挂起了,等待主程序执⾏完毕之后才会执⾏var a = 0setTimeout(function(){ a = 1},1000)console.log(a)解析本案例的同步代码会在while循环中阻塞2秒,所以console.log(a)这⾏代码会在2秒之后才能获得执⾏资源,但是最终输出的结果仍然是0。这是为什么呢?这⾥仍然可以通过JavaScript的运⾏模型来进⾏理解,由于单线程异步模型的规则是严格的同步在前异步靠后顺序,本案例的同步代码虽然阻塞了2秒,已经超过了setTimeout的等待时间,但是setTimeout中的宏任务到时间后,仅仅会被从⼯作线程移动到任务队列中进⾏等待。在时间到达1秒时,while循环没有执⾏结束,所以函数执⾏栈会被继续占⽤,直到循环释放并输出a之后,任务队列中的宏任务才能执⾏,所以这⾥就算setTimeout时间到了,也必须等待同步代码执⾏完毕,那么输出a的时候a=1的事件仍然没有发⽣,所以我们采⽤默认的上下结构永远拿不到异步回调中的结果,这也是为什么异步流程都是回调函数的原因。所以想要真正的在2秒后获取a的新结果的代码结构是这样的:到这⾥我们⼤概明⽩了回调函数的意义以及使⽤场景了,那么我们的Promise对象完整的结构是接下来案例中的样⼦,并且他是⼀个及特殊的存在,Promise中既包含同步的回调函数,⼜包含异步的回调函数。Promise案例介绍运⾏该案例并查看解析:var a = 0//依然使⽤setTimeout设置1秒的延迟设置a的值setTimeout(function(){ a = 1},1000)var d = new Date().getTime()var d1 = new Date().getTime()//采⽤while循环配合时间差来阻塞同步代码2秒while(d1-d<2000){ d1 = new Date().getTime()}console.log(a)//我们只有在这个回调函数中才能获取到a改造之后的结果var a = 0setTimeout(function(){ a = 1},1000)//注册⼀个新的宏任务,让他在上⼀个宏任务后执⾏setTimeout(function(){ console.log(a)},2000)参考上⾯的Promise对象结构,⼀个Promise对象包含两部分回调函数,第⼀部分是new Promise时候传⼊的对象,这段回调函数是同步的,⽽.then.catch.finally中的回调函数是异步的,这⾥我们提前记好。接下来可以在html⻚⾯中跑⼀遍程序,会发现这段程序并没有任何输出,然后我们可以将程序继续改造。练习案例:猜输出顺序将这段程序运⾏⼀下会发现输出顺序为:起步->调⽤resolve->结束->执⾏了resolve->then执⾏->finally执⾏再看下⾯的代码://实例化⼀个Promise对象var p = new Promise(function(resolve,reject){ })//通过链式调⽤控制流程p.then(function(){ console.log('then执⾏')}).catch(function(){ console.log('catch执⾏')}).finally(function(){ console.log('finally执⾏')})console.log('起步')var p = new Promise(function(resolve,reject){ console.log('调⽤resolve') resolve('执⾏了resolve')})p.then(function(res){ console.log(res) console.log('then执⾏')}).catch(function(){ console.log('catch执⾏')}).finally(function(){ console.log('finally执⾏')})console.log('结束')console.log('起步')var p = new Promise(function(resolve,reject){ console.log('调⽤reject') reject('执⾏了reject')})p.then(function(res){ console.log(res) console.log('then执⾏')}).catch(function(res){将这段程序运⾏⼀下会发现输出顺序为:起步->调⽤reject->结束->执⾏了reject->catch执⾏->finally执⾏解读Promise结构经过了上⾯的代码我们可以分析⼀下Promise的运⾏流程和结构,⾸先从运⾏流程上我们发现了new Promise中的回调函数确实是在同步任务中执⾏的,其次是如果这个回调函数内部没有执⾏resolve或者reject那么p对象的后⾯的回调函数内部都不会有输出,⽽运⾏resolve函数之后.then和.finally就会执⾏,运⾏了reject之后.catch和.finally就会执⾏。剖析对象结构Pomise对象相当于⼀个未知状态的对象,他的定义就是声明⼀个等待未来结果的对象,在结果发⽣之前他⼀直是初始状态,在结果发⽣之后他会变成其中⼀种⽬标状态,它的名字Promise中⽂翻译为保证。很多国外的电影台词都会涉及到Promsie这个单词,⽐如⼩明发现了邻居张三的妻⼦出轨了,在某天喝酒的时候⼩明和张三说:I sawyour wife played with other man! I promise ! I saw it!张三当然会说:No !shit!I can not trust you!dame!(语义⾃⾏理解),Promise在英⽂中是绝对保证的意思,所以在编程中Promise对象是⼀个⾮常严谨的对象,⼀定会按照约定执⾏,不会出现任务灵异问题(除使⽤不当外)。那么Promise本身具备三种状态:pending:初始状态,也叫就绪状态,这是在Promise对象定义初期的状态,这时Promise仅仅做了初始化并注册了他对象上所有的任务。fulfilled:已完成,通常代表成功执⾏了某⼀个任务,当初始化函数中的resolve执⾏时,Promise的状态就变更为fulfilled,并且then函数注册的回调函数会开始执⾏,resolve中传递的参数会进⼊回调函数作为形参。rejected:已拒绝,通常代表执⾏了⼀次失败任务,或者流程中断,当调⽤reject函数时,catch注册的回调函数就会触发,并且reject中传递的内容会变成回调函数的形参。三种状态之间的关系:Promise中约定,当对象创建之后同⼀个Promise对象只能从pending状态变更为fulfilled或rejected中的其中⼀种,并且状态⼀旦变更就不会再改变,此时Promise对象的流程执⾏完成并且finally函数执⾏。根据上⾯的分析,结合下⾯的代码案例学习Promise的规则,分析该对象的运⾏结果: console.log(res) console.log('catch执⾏')}).finally(function(){ console.log('finally执⾏')})console.log('结束')通过分析以上的说明我们知道了Promise对象存在三种状态以及他们之间的关系,那么我们在执⾏本段程序的时候会发现这个段代码的输出结果是:then执⾏->finally执⾏接下来查看另⼀个案例:我们在执⾏本段程序的时候会发现这个段代码的输出结果是:catch执⾏->finally执⾏再接下来查看下⾯的案例:我们在执⾏本段程序的时候会发现这个段代码的输出结果是:空new Promise(function(resolve,reject){ resolve() reject()}).then(function(){ console.log('then执⾏')}).catch(function(){ console.log('catch执⾏')}).finally(function(){ console.log('finally执⾏')})new Promise(function(resolve,reject){ reject() resolve()}).then(function(){ console.log('then执⾏')}).catch(function(){ console.log('catch执⾏')}).finally(function(){ console.log('finally执⾏')})new Promise(function(resolve,reject){}).then(function(){ console.log('then执⾏')}).catch(function(){ console.log('catch执⾏')}).finally(function(){ console.log('finally执⾏')})总结分析了对象结构和状态后,我们了解了Promise的异步回调部分如何执⾏,取决于我们在初始化函数中的操作,并且初始化函数中⼀旦调⽤了resolve后⾯再执⾏reject也不会影响then执⾏,catch也不会执⾏,反之同理。⽽在初始化回调函数中,如果不执⾏任何操作,那么promise的状态就仍然是pending,所有注册的回调函数都不会执⾏。