但是我们发现一个问题:
p()调用 =》
new Promise(executor),产生Promise对象 =》
executor执行 =》
异步任务setTimeout执行 =》
setTimeout是异步任务,会被挂起,JS主线程继续执行同步代码 =》
then方法被调用 =》
then方法中this.promiseState值为pending,因为setTimeout是异步任务还没有执行,导致resolve()没有调用,导致this.promiseState未被改变
被解耦的回调函数代码的执行,依赖于Promise对象的状态是完成态,
而Promise对象的状态完成依赖于异步任务的完成,
而异步任务的完成依赖于同步代码的结束,
而同步代码的结束依赖于被解耦的回调函数代码的执行。
死循环了。
未必,我们只要让上面流程中的一环闭环就行了,“被解耦的回调函数代码的执行”,这里我们不能强制要求回调函数的代码执行,因为理论上来说,回调函数的代码一定要在异步任务完成后执行。所以既然异步任务在等这里的同步代码结束,那我们就把回调函数的代码再还回去。
当Promise对象调用then方法,发现this.promiseState为pending时,表示封装的任务一定是异步任务,那么我们就把回调函数再还回去,等待异步任务结束执行。
因为异步任务一定会去调用resolve或reject方法修改this.promiseState,所以我们可以在resolve或reject中执行还回去的回调函数。
至此我们就完成了Promise的异步封装,实现了异步任务和回调函数的解耦。
Promise实现异步任务间串行执行,即异步任务间的同步执行
==============================
传统的JS异步任务,必须要通过回调函数嵌套异步任务,才能实现异步任务之间串行执行。当嵌套层级过深,就会形成回调地狱,导致代码结构臃肿,不易改动。
Promise的出现,让异步任务和其回调函数解耦,回调函数不用定义在异步任务原有框架中,可以在异步任务框架定义。这就从结构上杜绝了回调地狱的可能。
在逻辑上:
当前被解耦的回调函数是随着Promise.prototype.then调用而被传递给改变Promise状态的方法resolve和reject,异步任务的执行触发了resolve和reject的调用,而resolve和reject的调用触发了被解耦的回调函数的调用。
所以可以简单理解为:随着then方法的调用,被解耦的回调函数也被调用了
即我们想要执行异步任务的回调函数,就调用then方法。而then方法本身是同步任务。
那么如果有多个被Promise封装的异步任务,是不是可以通过分别调用then方法来实现串行执行呢?
function Promise(executor){
this.promiseState = ‘pending’
this.promiseResult = null
this.callback = null
let that = this
function resolve(data) {
that.promiseState = ‘fulfilled’
that.promiseResult = data
if(that.callback) {
that.callback.onResolve()
}
}
function reject(data) {
that.promiseState = ‘rejected’
that.promiseResult = data
if(that.callback) {
that.callback.onReject()
}
}
executor(resolve,reject)
}
Promise.prototype.then = function(onResolve, onReject){
if(this.promiseState === ‘fulfilled’) {
onResolve()
} else if(this.promiseState === ‘rejected’) {
onReject()
} else {
this.callback = {
onResolve : onResolve,
onReject : onReject
}
}
}
function p1() {
return new Promise(
function(resolve,reject){
try {
setTimeout(function(){
resolve(‘OK’)
},1000)
} catch(e) {
reject(‘NOK’)
}
}
)
}
function p2() {
return new Promise(
function(resolve,reject){
try {
setTimeout(function(){
resolve(‘OK’)
},2000)
} catch(e) {
reject(‘NOK’)
}
}
)
}
function p3() {
return new Promise(
function(resolve,reject){
try {
setTimeout(function(){
resolve(‘OK’)
},3000)
} catch(e) {
reject(‘NOK’)
}
}
)
}
p1().then(function(value){
console.log(new Date,‘a’)
},function(reason){
console.log(reason)
})
p2().then(function(value){
console.log(new Date,‘b’)
},function(reason){
console.log(reason)
})
p3().then(function(value){
console.log(new Date,‘c’)
},function(reason){
console.log(reason)
})
答案是不行的,因为then方法虽然是同步的,但是then方法的参数,即解耦的回调函数的执行不是then方法触发的(这里指异步情况,如果Promise封装的是同步任务,那这里回调函数就是then方法触发),then方法只负责将回调函数保存到Promise对象的callback属性中,当异步操作完成,异步任务就会调用resolve方法或reject方法,后面resove或reject方法会负责调用回调函数。
即:真正触发异步解耦的回调函数的调用的还是异步操作的完成。
那么如何保证then方法中的回调函数执行完,才继续执行下个同步任务呢?
Promise对then方法做了如下设计:
then方法可以返回一个新的Promise对象,可以用来作为异步任务串行调用过程中下次then的调用者。
即:下一次then的调用者是上一次then的返回值,而then的返回值又取决于其形参回调函数的返回值。
这样就形成了一个依赖关系:
下次then的调用的调用者,依赖于上次then的返回值,
上次then的返回值,依赖于本身形参(回调函数)的返回值
回调函数的返回值,依赖于resolve或reject函数的执行
resolve或reject函数的执行,依赖于异步操作的完成,
所以下次then的调用必须要等上次then回调函数执行完成,即等上次then对应的异步任务的完成
then方法返回的Promise对象生成逻辑如下:
当回调函数的返回值是一个Promise对象时,then就返回一个和该Promise对象有相同状态和结果的新的Promise对象
当回调函数的返回值是一个非Promise对象时,then就返回一个fulfilled状态,以及将回调函数返回值作为结果的新的Promise对象
function Promise(executor){
this.promiseState = ‘pending’
this.promiseResult = null
this.callback = null
let that = this
function resolve(data) {
that.promiseState = ‘fulfilled’
that.promiseResult = data
if(that.callback) {
that.callback.onResolve()
}
}
function reject(data) {
that.promiseState = ‘rejected’
that.promiseResult = data
if(that.callback){
that.callback.onReject()
}
}
executor(resolve,reject)
}
Promise.prototype.then = function(onResolve, onReject){
let that = this
return new Promise((resolve,reject)=>{
function common§ {
if(p instanceof Promise){
p.then(v => {
resolve(v);
}, r => {
reject®;
})
} else {
resolve§
}
}
if(that.promiseState === ‘fulfilled’) {
let p = onResolve(that.promiseResult)
common§
} else if(that.promiseState === ‘rejected’) {
let p = onReject(that.promiseResult)
common§
} else {
that.callback = {
onResolve : function(){
let p = onResolve(that.promiseResult)
common§
},
onReject : function() {
let p = onReject(that.promiseResult)
common§
}
}
}
})
}
function p1() {
return new Promise(
function(resolve,reject){
try {
setTimeout(function(){
resolve(‘OK’)
},1000)
} catch(e) {
reject(‘NOK’)
}
}
)
}
function p2() {
return new Promise(
function(resolve,reject){
try {
setTimeout(function(){
resolve(‘OK’)
},2000)
} catch(e) {
reject(‘NOK’)
}
}
)
}
function p3() {
return new Promise(
function(resolve,reject){
try {
setTimeout(function(){
resolve(‘OK’)
},3000)
} catch(e) {
reject(‘NOK’)
}
}
)
}
p1().then(v=>{
console.log(new Date,‘a’)
return p2()
}).then(v=>{
console.log(new Date,‘b’)
return p3()
}).then(v=>{
console.log(new Date,‘c’)
})
这就是Promise实现异步任务串行执行的原理。
Promise的其他特性
1、Promise对象的状态只能修改一次,即只能从pending-》fulfilled或pending-》rejected
Promise对象的状态修改,是通过resolve和reject方法,所以只要在resolve和reject方法执行前判断Promise对象的状态是pending即可,若不是pending,则状态已经被修改过,不能再次修改
function Promise(executor){
this.promiseState = ‘pending’
this.promiseResult = null
this.callback = null
let that = this
function resolve(data) {
if(that.promiseState !== ‘pending’) return
that.promiseState = ‘fulfilled’
that.promiseResult = data
if(that.callback) {
that.callback.onResolve()
}
}
function reject(data) {
if(that.promiseState !== ‘pending’) return
that.promiseState = ‘rejected’
that.promiseResult = data
if(that.callback){
that.callback.onReject()
}
}
executor(resolve,reject)
}
Promise.prototype.then = function(onResolve, onReject){
let that = this
return new Promise((resolve,reject)=>{
function common§ {
if(p instanceof Promise){
p.then(v => {
resolve(v);
}, r => {
reject®;
})
} else {
resolve§
}
}
if(that.promiseState === ‘fulfilled’) {
let p = onResolve(that.promiseResult)
common§
} else if(that.promiseState === ‘rejected’) {
let p = onReject(that.promiseResult)
common§
} else {
that.callback = {
onResolve : function(){
let p = onResolve(that.promiseResult)
common§
},
onReject : function() {
let p = onReject(that.promiseResult)
common§
}
}
}
})
}
2、一个Promise对象可以调用多次then方法,且后面的不会覆盖前面的
一个Promise对象的多次then方法调用,当前代码是会发生覆盖的,因为对于异步任务封装而言,Promise对象调用then方法就是将回调函数保存到Promise对象callback属性中,等待异步任务完成后调用。
一个Promise对象的多次then方法调用,就是将多个then方法保存到Promise对象callback属性中,当前callback属性设计就是一个对象,所以每次then调用都会导致callback属性指向改变,即覆盖现象。
function Promise(executor){
this.promiseState = ‘pending’
this.promiseResult = null
this.callback = []
let that = this
function resolve(data) {
if(that.promiseState !== ‘pending’) return
that.promiseState = ‘fulfilled’
that.promiseResult = data
if(that.callback) {
that.callback.forEach(item=>{item.onResolve()})
}
}
function reject(data) {
if(that.promiseState !== ‘pending’) return
that.promiseState = ‘rejected’
that.promiseResult = data
if(that.callback){
that.callback.forEach(item=>{item.onReject()})
}
}
executor(resolve,reject)
}
Promise.prototype.then = function(onResolve, onReject){
let that = this
return new Promise((resolve,reject)=>{
function common§ {
if(p instanceof Promise){
p.then(v => {
resolve(v);
}, r => {
reject®;
})
} else {
resolve§
}
}
if(that.promiseState === ‘fulfilled’) {
let p = onResolve(that.promiseResult)
common§
} else if(that.promiseState === ‘rejected’) {
let p = onReject(that.promiseResult)
common§
} else {
that.callback.push({
onResolve : function(){
let p = onResolve(that.promiseResult)
common§
},
onReject : function() {
let p = onReject(that.promiseResult)
common§
}
})
}
})
}
3、当我们指定的executor函数,回调函数出现异常时,我们应该做容错机制,此时应该产生一个状态为rejected,结果为异常信息的Promise对象
function Promise(executor){
this.promiseState = ‘pending’
this.promiseResult = null
this.callback = []
let that = this
function resolve(data) {
if(that.promiseState !== ‘pending’) return
that.promiseState = ‘fulfilled’
that.promiseResult = data
if(that.callback) {
that.callback.forEach(item=>{item.onResolve()})
}
}
function reject(data) {
if(that.promiseState !== ‘pending’) return
that.promiseState = ‘rejected’
that.promiseResult = data
if(that.callback){
that.callback.forEach(item=>{item.onReject()})
}
}
try {
executor(resolve,reject)
} catch (e) {
reject(e)
}
}
Promise.prototype.then = function(onResolve, onReject){
let that = this
return new Promise((resolve,reject)=>{
function common§ {
if(p instanceof Promise){
p.then(v => {
resolve(v);
}, r => {
reject®;
})
} else {
resolve§
}
}
if(that.promiseState === ‘fulfilled’) {
try {
let p = onResolve(that.promiseResult)
common§
} catch (e) {
reject(e)
}
} else if(that.promiseState === ‘rejected’) {
try {
let p = onReject(that.promiseResult)
common§
} catch (e) {
reject(e)
}
} else {
that.callback.push({
onResolve : function(){
try {
let p = onResolve(that.promiseResult)
common§
} catch (e) {
reject(e)
}
},
onReject : function() {
try {
let p = onReject(that.promiseResult)
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
promiseResult)
common§
} catch (e) {
reject(e)
}
} else if(that.promiseState === ‘rejected’) {
try {
let p = onReject(that.promiseResult)
common§
} catch (e) {
reject(e)
}
} else {
that.callback.push({
onResolve : function(){
try {
let p = onResolve(that.promiseResult)
common§
} catch (e) {
reject(e)
}
},
onReject : function() {
try {
let p = onReject(that.promiseResult)
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-BBDmjPFq-1715540854401)]
[外链图片转存中…(img-aG1QQ1hc-1715540854401)]
[外链图片转存中…(img-spsNZ9B9-1715540854402)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!