回调函数和Promise
想要理解Promise和Aysnc(Aysnc、Await是搭配使用的,所以下文的Aysnc默认包含了Aysnc和Await关键字),首先要理解JavaScript中异步的概念。
// 传统写法
let callback = function(value){
console.log(value)
}
let result = step1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
//执行回调
callback(value4)
});
});
});
});
console.log(result)
// Promise 的写法
let result = (new Promise(step1)).then(step2).then(step3).then(step4);
console.log(result)
在正常的逻辑下,是无法实现异步的,为了实现异步,JavaScript中引入了回调函数(callback)的概念。也就是通过层层嵌套,最终的执行结果在回调函数中返回。
为了解决嵌套过多,解决代码不美观不易读的问题,Promise就出现了。Promise想要解决的问题就是干掉回调函数,异步执行的代码可以写成同步执行的形式。
Promise必须和Aysnc、Await搭配使用
在使用Promise的地方必须使用Await进行标识,表明这是一个异步函数,必须等待Promise中的流程执行结束之后,才能执行后面的流程。
而Aysnc则应该写在标识有Await的外层函数上。
async function step1(value) {
let value1 = await step2(value);
}
Aysnc和Await的用法就是这么简单,告诉主函数这个函数是异步执行的,必须等它执行完成后才能进入到下一部流程。Aysnc和Await起到的是声明的作用,真正实现异步的逻辑其实是由Promise来承担的。
function step1(param) {
return new Promise(function(resolve, reject) {
//执行耗时的操作 比如mysql查询
mysql_query(sql, [param]).then((r) = >{
resolve(r)
}).catch(e = >{
reject(e)
})
}).then((r) = >{
console.log('resolve', r) return 'success result'
}).catch((e) = >{
console.log(e) return 'failed result'
});
}
一个完整的Promise对象由三部分组成:
- 参数resolve, reject。这个部分可以使从外部传入的回调函数callback,也可以只是简单地标识。如果是简单地标识,那么正确执行之后resolve➡then,出现错误的的话reject➡catch。
- then已经不属于Promise的主体,而是顺序执行的下一步。也就是说如果有必要,直接在Promise中传入参数,resolve之前return返回值,最终也能够收到结果。如果没有return,又有then的话,then的部分还会执行。
- catch是链式写法的一部分,在Promise的主体中reject之后,就会执行catch这一部分。有趣的是Promise后紧跟的catch(链式写法),只管Promise的部分,甚至内部的抛出的异常它也没办法捕获。所以mysql_query后面也写了catch,用于捕获mysql_query自身错误。否则mysql_query出错,会报unhandledpromiserejectionwarning异常。
Promise All的用法
你有没有注意到Promise方法内部并不是立即执行返回的,而是新建并返回了一个Promise对象,这就是异步的精髓。因为在写代码的时候,返回值什么时候能够取到(web请求什么时候成功,SQL查询会不耗时太久等)我们是无法控制的,我们只能把获取结果的渠道建立后,最终的结果就会回到正轨。
但是Promise针对的是单个任务,如果需要批量处理的业务,那么仅仅通过Promise就无法解决。
async function someRequest() {
let result = true
for (let i = 0; i < 10; i++) {
result && await step1(i)
}
return result
}
比如这样的一串异步请求,我试图通过多个异步请求的结果,判断最终有没有全部都执行成功了。但是通过for循环执行时,第1个请求,第2,3,4...第n个请求之间是没有先后关系的,系统不会等第1个执行完了,再顺序执行第2,3,4...第n个,如果是这样也无法体现出批量的意义。
而是同时发出n个请求,也就是同时new n个Promise,然后所有Promise同步执行。事实上还没等所有的await执行结束,for循环就遍历完成,然后return result了。
异步是指,结果的返回时间、执行成功与否我们无法控制。同步是指我们不关心返回的结果是什么,而是立即返回,在还没有从等待进程中获得结果的情况下,我们就按照初始化的情况返回。
为了解决多个Promise异步的问题,Promise All就出现了。
//统一执行await
async function dbFuc(db) {
let docs = [{}, {}, {}];
let promises = docs.map((doc) => db.post(doc));
let results = await Promise.all(promises);
console.log(results);
}
//批量执行await
async function dbFuc(db) {
let docs = [{}, {}, {}];
let promises = docs.map((doc) => db.post(doc));
let results = [];
for (let promise of promises) {
results.push(await promise);
}
console.log(results);
}
统一执行和批量执行的结果都是一样的,目的保证所有Promise对象都执行完成后,拿到结果数组results之后,才能执行后续的流程。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。