异步定义
任务A执行到一半,暂停,去执行另外一段任务B,完了再回到断点处继续执行A,这是异步编程最直观的理解。
假定一个场景
- JS读取文件a.txt,获取到数据dataA=1;
- 再读取文件b.txt,获取到数据dataB=2;
- 再读取文件c.txt,获取到数据dataC=3;
- 求和 sum = 6。
旧的回调函数解决异步操作
function successCallback(result){
cosnole.log('成功' + result)
}
function failureCallback(error){
console.log('失败' + error)
}
createAudioFileAsync(set,successCallback,failureCallback)
promise
定义:Promise是ES6中提供异步编程的解决方案,可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
具体表达:
- 语法上:Promise是一个构造函数
- 功能上:Promise对象用来封装一个异步操作并可以获取其结果
promise流程
promise解决了哪些开发中的痛点
- 回调地狱,代码难以维护,
第一个的函数的输出是第二个函数的输入这种现象
,可读性差
doSomething(function(result){
doSomethingElse(result,function(newResult){
doThirdThing(newResult,function(finalResult){
console.log('得到数据')
},failureCallback)
},failureCallback)
},failureCallback)
-
promise可以支持多个并发的请求,获取并发请求中的数据
-
promise可以解决异步的嵌套带来的可读性的问题
doSomething().then(function(result){
return doSomethingElse(result)
})
.then(function(newResult){
return doThirdThing(newResult)
}).then(function(finalResult){
console.log('数据')
}).catch(failureCallBack)
- promise 更加灵活,旧的回调函数,必须在启动异步任务前指定。promise:启动异步任务 =>返回promise对象=>给promise对象绑定回调函数
如何使用promise
- 创造一个Promise实例
- Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数
- 可用Promise的try和catch方法预防异常
// 创建一个新的Promise对象
const p = new Promise((resolve,reject) =>{ //执行期函数
// 执行异步操作任务
setTimeout(() => {
const time = Date.now()
if(time %2 == 0 ){
resolve('成功的数据')
}else{
reject('失败的数据')
}
},1000)
})
p.then(
value =>{ //接收得到成功的value数据
console.log('成功')
}
reason =>{ // 接收得到失败的reason数据
console.log('失败')
}
)
promise特点:
1.对象的状态不受外界影响:只有异步操作的结果才能修改内部状态。 Promise共有三种状态。 Pending(进行中),Fulfilled(成功),Rejected(已失败)。
2.一旦状态改变,就不会再变更: 状态变更 Pending =>Resolved 或者 Pending => Rejected。 成功的结果数据一般称为value,失败的结果数据一般称为reason
Promise缺陷:
1:一旦新建,就会立即执行,无法取消。
2:如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。不能使用 try catch 来捕捉异常。
3: 当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
4:同async 用法相比,大量then的使用,代码较为繁琐。
Promise基本用法:
Promise是一个对象,生成Promise实例,构造函数接收一个函数作为参数。Promise实例使用then来处理回调。then可以接收两个参数,第一个是成功后的回调,第二个是失败时的回调(非必填)。
1:创建后会立即执行 2:Promise 的链式调用。 使用 then 和 catch 方法实现链式调用。虽然then 的入参有两个,但是失败状态不建议在then内部实现,因为如果是链式调用的话,会造成代码繁琐,采用 catch方法,可简化实现方式。 采用统一抛出异常方式 ,在链式调用后catch方法内捕捉。避免每个then,都去实现 reject的回调。
Promise方法:
-
Promise.then
- .then 它自己的执行结果是一个 promise。这意味着我们可以链接任意数量的 .then,前一个 then 回调的结果将会作为参数传递给下一个 then 回调.
-
Promise.all
- 将多个Primise 实例合并成一个实例,只有当所有实例都变成resolved状态,或者其中一个变成rejected状态,才会执行回调。
Promise.all() 方法接收一个 promise 的 iterable 类型(注:Array,Map,Set都属于ES6的iterable类型)的输入,并且只返回一个[
Promise
]实例, 那个输入的所有 promise 的 resolve 回调的结果是一个数组。这个[Promise
]的 resolve 回调执行是在所有输入的 promise 的 resolve 回调都结束,或者输入的 iterable 里没有 promise 了的时候。 它的 reject 回调执行是,只要任何一个输入的 promise 的 reject 回调执行或者输入不合法的 promise 就会立即抛出错误,并且reject的是第一个抛出的错误信息。
- 将多个Primise 实例合并成一个实例,只有当所有实例都变成resolved状态,或者其中一个变成rejected状态,才会执行回调。
Promise.all() 方法接收一个 promise 的 iterable 类型(注:Array,Map,Set都属于ES6的iterable类型)的输入,并且只返回一个[
-
Promise.race()
- 是将多个Promise实例,包装成一个新的Promise实例,多个实例有一个实例状态改变,实例状态就改变了,返回的第一个改变的实例状态。
-
done()
- Promise对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。因此,我们可以提供一个done方法,总是处于回调链的尾端,保证抛出任何可能出现的错误。
-
finally()
- finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。它与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。实例可以用来网络请求中的loading
promise.then()返回的新promise的结果状态由什么决定
1)简单表达:由then()指定的回调函数执行的结果决定.
2)详细表达:
- 如果抛出异常,新promise变为rejected,reason为抛出的异常
- 如果返回的是非promise的任意值,新promise变为resolved,value为返回的值
- 如果返回的是另一个新promise,此promise的结果就会成为新的promise的结果
new Promise((resolve,reject)=>{
resolve(1)
}).then(
value => {
console.log('onResolved()',value) // 1
},
reason => {
console.log('onRejected()',reason)
}
).then(
value =>{
console.log('onResolved()',value) // undefined
},
reason => {
console.log('onRejected()',reason)
}
)
总结:1 undefined
promise异常传透?
- 当使用promise的then链式调用时,可以在最后指定失败的回调
- 前面任何操作除了异常,都会传到最后失败的回调中处理
中断promise链
- 当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数
- 办法:在回调函数中返回一个pending状态的promise对象
async / await (用同步方式,异步执行操作)
async---定义异步函数(async function someName(){...})
-
async 位于函数字面量或函数表达式的前面(普通函数、立即执行函数和箭头函数均可),被修饰函数执行后会返回一个 Promise 对象。
-
函数体内返回值则作为 resolve 的参数。
-
函数体内抛出错误,则错误信息作为 reject 的参数。
async function test() {
console.log("test");
return 20;
}
// 等价于
function test() {
return new Promise((resolve, reject) => {
console.log("test");
resolve(20);
});
}
await--- 暂停异步函数的执行 (var result = await someAsyncCall();)
-
await 一定要位于 async 函数内部。
-
await 一般位于 Promise 对象之前,所以一般位于 async 函数执行的前面,但若是返回值为 Promise 对象的普通函数也可。
-
await 会拿到该对象的结果,也就是 then 中 resolve 或 reject 的参数。如果不是 Promise 对象,则直接返回对应的值。
-
若 await 后方不是 Promise 对象,则会将其用 Promise.resolve 包装后执行。
-
await 的执行会被强制等待至拿到结果,后续函数体内的代码执行被阻塞,函数本身不会阻塞整体代码
// 定义一个返回Promise对象的函数
function fn() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(30);
}, 1000);
})
}
// 然后利用async/await来完成代码
const foo = async () => {
const t = await fn();
console.log(t);
console.log('next code');
}
foo();
// result:
// 30
// next code
解析:
当在async函数中,运行遇到await时,就会等待await后面的函数运行完毕,而不会直接执行后面的next code。
async和await的作用
- 简化 Promise 的使用过程。
- 让你的异步代码看起来像是同步的
- async和await是用来处理异步的。即你需要异步像同步一样执行,需要异步返回结果之后,再往下依据结果继续执行。
- async 是“异步”的简写,而 await 可以认为是 async wait 的简写。
- async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。
异常处理
在Promise中,我们知道是通过catch的方式来捕获异常。而当我们使用async时,则通过try/catch来捕获异常。
// promise 写法
function fn() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('some error.');
}, 1000);
})
}
// async写法
const foo = async () => {
try {
await fn();
} catch (e) {
console.log(e); // some error
}
}
foo();
如果有多个await函数,那么只会返回第一个捕获到的异常。
function fn1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('some error fn1.');
}, 1000);
})
}
function fn2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('some error fn2.');
}, 1000);
})
}
const foo = async () => {
try {
await fn1();
await fn2();
} catch (e) {
console.log(e); // some error fn1.
}
}
foo();
额外补充
如果promise发生异常,则会进到then第二个参数的回调函数,如果这个参数不存在,则进到catch中。
let p = new Promise((resolve, reject) => {
throw new Error('some errors')
// resolve()
// reject()
});
p.then(res => {
console.log('1111')
},res => {
console.log('2222')
}).catch(e => {
console.log('3333')
})
/**
* 结果为:
* 2222
*/
在一个失败操作(即一个 catch)之后可以继续使用链式操作,即使链式中的一个动作失败之后还能有助于新的动作继续完成。
new Promise((resolve, reject) => {
console.log('Initial');
resolve();
})
.then(() => {
throw new Error('Something failed');
console.log('Do this');
})
.catch(() => {
console.log('Do that');
})
.then(() => {
console.log('Do this whatever happened before');
})
输出结果: Initial
Do that
Do this whatever happened before