头图

回调函数和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之后,才能执行后续的流程。


grofis
17 声望1 粉丝

一个努力变得有用的程序员!