同步和异步
同步
概念:必须等到前一个任务执行完毕之后再执行后一个任务,执行结果按照代码的先后顺序执行,通俗一点来讲,就类似于食堂排队打饭,前一个同学没有打完饭,后一个同学就不能打饭,必须等到前一个同学打完饭才能让后一个同学打饭。
同步任务 指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务.
异步
概念:当前任务的的执行不会阻塞后续任务的执行,我们也可以简单地理解为:可以改变程序正常执行顺序的操作就可以看成是异步.
异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有等主线程任务执行完毕,"任务队列"开始通知主线程,请求执行任务,该任务才会进入主线程执行。
所有的事件绑定都是异步任务。
Promise
概念:是异步编程的一种新的解决方案(旧的是谁 ?纯回调),解决了以前链式异步调用形成的回调地狱问题。
具体理解:从语法上来说:promise是一个构造函数;
从功能上来说:promise(实例)对象用来封装一个异步操作并可以获取其结果。
Promise的优缺点
优点:
1.指定回调函数的方式更加灵活
纯回调函数:必须在启动异步任务之前
//纯回调函数可能会出现回调地狱的问题:回调函数不断嵌套,什么时候会执行回调嵌套呢?当出现多个串联的异步操作时,而且纯回调函数不便于阅读,也不便于异常处理
promise:启动异步任务=>返回promise对象 =>给promise对象绑定回调函数(甚至可以在异步)
2.支持链式调用,可以解决回调地狱问题
//伪代码
One().then((result)=> {
return two(result)
}).then((secondResult)=>{
return three(secondResult)
}).then((finalResult)=>{
console.log("这是最后一次的返回结果:"+finalResult)
}).catch(failureCallback)
一旦得到了promise 的新对象,就说明启动了一个异步任务,通过调用.then方法又返回一个新的promise对象中的异步任务中返回的结果,将它作为下一个.then方法的参数.
补充:
能够最终解决回调地狱的问题: async/await ; 纯粹的同步编码方式:(没有回调函数的使用)
//伪代码
async function request() {
try{
const result=await one();
const secondResult=await two(result);
const finnalResult=await three(secondResult);
console.log("这是最后一次的返回结果:"+finnalResult);
}catch(err){
failureCallback(err)
}
}
缺点:
1.无法取消Promise,一旦新建它就会立即执行,无法中途取消。
2.如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
3.当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
promise的三个状态
1.pending(待定):等待态
2.fulfilled(完成):完成态
3.rejected(拒绝):拒绝态
状态改变只有两种可能:
pendding => fulfilled
pendding => rejected
一个promise对象只能改变一次状态,无论是成功还是失败都会有一个结果数据,成功的数据被称之为’value’,失败的数据结果被称之为’reason’
如何改变promise的状态:
resolve(value):如果当前是pendding就会变为fulfilled;
reject(reason):如果当前是pending就会变为rejected
抛出异常:如果当前是pending就会变成rejected
Promise的基本使用流程:
<script>
const p = new Promise((resolve, reject) => { //执行器函数 同步回调
//异步函数执行语句
setTimeout(() => {
const time = Date.now();
//如果 当前时间是奇数,就代表成功
if (time % 2 == 1) {
//成功 调用resolve函数,返回一个正确的value数据
resolve('成功的奇数时间是:'+time)
} else {
//失败 调用rejected函数,返回一个错误的reason数据
reject('失败的偶数时间是:'+ time)
}
},1000)
})
//调用then方法接收成功的value数据,接收失败的reason数据
p.then(
value => {
//接收成功的value数据
console.log('成功的回调', value)
}, reason => {
//接收失败的reason数据
console.log('失败的回调', reason)
}
)
</script>
如何使用Promise
1.promise使用的API(可暂时理解为语法)
Promise构造函数:Promise(excutor){}
excutor函数:同步执行(resolve,reject)=>{}
resolve函数:内部定义成功时调用的函数 value=>{}
reject函数:内部函数定义失败时调用的函数 reason =>{}
说明:excutor函数会在Promise内部立即同步回调,异步操作在执行器中执行
Promise.prototype.then方法:(onResolved,onRejected)=>{}
onResolved函数:成功时调用的函数(value)=>{}
onRejected函数:失败时调用的函数(reason)=>{}
说明:指定用于得到成功value的成功回调和用于得到失败reasonde的失败回调
返回一个新的Promise对象
Promise.prototype.catch方法: (onRejected)=>{}
onRejected函数:失败的回调函数(reason)=>{}
说明:then()的语法糖,相当于:then(undefined,onRejected)
Promise.prototype.finally方法:
说明:返回一个新的Promise对象,无论结果成功还是失败都会返回
Promise.resolve方法:(value)=>{}
value:成功的数据或Promise对象
说明:返回一个成功或者失败的Promise对象
Promise.reject方法: (reason)=>{}
reason:失败的原因
说明:返回一个失败的Promise对象
Promise.all方法:(promises)=>{}
promises:包含n个promises数组
说明:返回一个新的promise,只有所有的prmise都成功才成功,成功时返回的结果也是一个数组,只要有一个失败了就直接失败了
Promise.race方法:(promises)=>{}
promises:包含n个promise的数组
说明:返回一个新的promise,第一个完成的promise的结果状态就会说最终结果的状态。
改变promise状态和指定回调函数的先后问题
都有可能,正常情况下是先指定回调函数再改变状态,但也乐意先改变状态再指定回调函数
1.先改变状态后指定回调函数
在执行器函数中直接调用resolve()/reject()
或
延迟更长的的时间采去调用then()
关于什么时候得到数据?
如果先指定回调函数。当状态发生改变时,回调函数会被调用,函数被执行;
如果先改变状态,指定回调函数是,回调函数就会被调用,得到数据
new Promise((resolve,reject))=>{
setTimeout( ()=>{
resolve(1) //后改变状态 同时指定数据,在异步执行回调函数
},1000);
}).then(//一般情况下是先指定回调函数,保存当前指定的回调函数
(value)={
console,log('value1',resolve)},
(reason)=>{
console.log('reason1',reason)}
)
//先改变状态 后指定回调函数
new Promise((resolve,reject))=>{
resolve(1)//先改变状态(同时指定数据)
}).then(//后指定回调函数,异步执行回调函数
(value)={
console,log('value2',resolve)},
(reason)=>{
console.log('reason2',reason)}
)
(重点)promise.then()返回的新的Promise对象的结果是由什么状态决定的
由then()指定的回调函数执行的结果决定的
具体来说就是:
- 如果抛出异常,新的promise变为rejected,reason为抛出的异常;
- 如果return 返回的是非promise的任意值(例如:数字),新的promise对象会变为fulfilled,value中最后返回的值为结果,没有return 返回结果就是undefined;
- 如果返回的是另一个新的promise,此promise的结果就会成为新promise的结果
<script>
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
console.log('onResolved1', value)
// return 2;
// return Promise.resolve(3);
//return Promise.reject(4);
//throw 5;
},
reason => {
console.log('onRejected1', reason)
}
).then(
value => {
console.log('onResolved2', value)
},
reason => {
console.log('onRejected2', reason)
}
)
</script>
promise如何串联多个任务
- 利用 promise的then()返回一个新的promise,可以形成then的链式调用,通过then的链式调用串联多个同步或者异步任务
代码实现如下:
<script>
new Promise((resolve, reject) => {
setTimeout(()=>{
console.log('异步任务一')
resolve(1)
},1000)
}).then(
value => {
console.log('任务一的结果:', value)
console.log('同步任务二')
return 2;
}
).then(
value => {
console.log('任务二的结果:', value)
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log('异步任务三');
resolve(3)
},1000)
})
}
).then(
value => {
console.log('任务三的结果:', value)
}
)
</script>
promise的异常传透(逐层传递)
当使用promise的链式调用时,可以在最后指定失败的回调,前面操作出了任何异常都会传到最后的失败回调中处理,但要排除then中包含了错误处理的情况
.then()返回的promise,只有两种情况会失败,要么抛出异常(throw error),要么返回一个失败的promise(Promise.reject())
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2;
},
//当没有异常情况处理时,内部会默认抛出一个异常
//reason => {throw reason}或者手动添加 return Promise.reject(1)
).then(
value => {
console.log('onResolved2()', value)
return 2;
},
reason => {throw reason}
).then(
value => {
console.log('onResolved3()', value)
return 3;
},
reason => {throw reason}
).catch((reason)=>{
console.log('onRejected1()',reason)
})
如何中断promise链
当使 用promise的链式调用时,在中间中断,不再调用后面 的回调函数,可以让当前的回调函数返回一个处于等待态pending的promise对象
new Promise((resolve, reject) => {
//resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2;
},
//当没有异常情况处理时,内部会默认抛出一个异常
//reason => {throw reason}或者 手动添加 return Promise.reject()
).then(
value => {
console.log('onResolved3()', value)
return 3;
},
reason => {throw reason}
).catch((reason)=>{
console.log('onRejected1()',reason)
//中断promise的链式调用 后面的.then方法(回调函数)不再执行
return new Promise(()=>{})//返回一个处于pending态的promise对象
}).then(
value => {
console.log('onResolved3()', value)
return 3;
},
reason => {throw reason}
)