JavaScript - Promise异步

看过很多关于Promise的文章、但想真正了解和在项目中使用、还是需要自己、撸一次代码才能真的了解使用。

本文从回调地域到常用的异步解决方案逐步了解;

回调地狱

回调地狱 术语通常用来描述

function asyncTask(){
    asyncA(function(flag){
        if(flag)
        {
            return run(flag);
        }
        asyncB(function(flag){
                if(flag)
                {
                    return run(flag);
                }
                ...
            })

    })
}

这样的回调使得,难以维护代码,和管理流程变得困难。

 

Promise

ES6中Promise 提供了一种异步编程解决方案,比传统的回调函数相比更合理、强大;

  • Promise对象是个构造函数,用来生成Promise实例。
  • Promise构造函数接受一个函数作为参数,该函数有两个参数 resolvereject
  • resolve函数作用是将当前Promsie对象的状态改为从”未完成”变为”完成”,在异步操作成功时调用,并将返回的接口作为参数传递给resolve函数,reject函数作用将当前Promsie对象的状态从”未完成”变为”失败”,在异步操作失败时调用。
  • Promise实例生成以后,由于Promsie可以链式调用,可以在声明后面添加then()方法,方法接受两个参数作为回调函数,第一个参数resolve成功时,第二个reject失败时调用函数;
let is = true;
function run()
{
        //声明实例
    const promise = new Promise((resolve,reject)=>{
        if(is)
        {
            resolve('done');  //成功
        }
        else
        {
            reject('fail');  //失败
        }
    })
    .then((a)=>{    //处理结果
        console.log(a);
    },
    (a)=>{
        console.log(a);
    });
};
run(); //done

 

可以看出Promise脱离了回调地域,异步任务完成后,通过实例.then()方法,执行相应结果的回调函数

在执行Promise任务时,当代码发生错误时、通常我们可以在最后使用.catch()来做个兜底处理错误作用。

let s = new Promise((done,fail)=>{
        throw "error";
    });
    
    s.then(done=>{
        console.log(done);
    },
    fail=>{
        console.log(fail);   //错误 error
    })
    .catch(msg=>{
        console.log(`错误`,msg);   
    });

但其实并不然,Promise作为链式处理结果,其catch是s.then返回的Promise、而不是s所返回的,实际错误会被then里的回调函数所执行。

catch() 方法返回一个Promise,并且处理拒绝的情况。它的行为与调用Promise.prototype.then(undefined, onRejected) 相同。 (事实上, 调用 obj.catch(onRejected) 内部calls obj.then(undefined, onRejected)).

引用MDN文档、reject和catch是相同的、其都会调用onRejected。

如果Promise在resolve后面报错、不会被捕获,同时也不会响应回调函数,因为在promise状态确定就无法再改变;

Promise.all

在真实的场景中,我们需要执行的异步可能不只一个可能很多,他们有相应的依赖,如果其中一个出现错误,可能需要通知用户。虽然Promise相比之前回调函数已经非常简洁、但是过多的话、看上去还是有点凌乱、难以理解。

Promise.all方法,当所有参数内的Promise实例执行成功(resolve)时,返回一个Promise(resolve)成功回调,反之如其中一个Promise实例失败(reject),则立即返回Promise(reject)失败回调。

let fetch1 = Promise.resolve(33);
let fetch2 = Promise.resolve(22);
let fetch3 = new Promise(function(resolve, reject) {
    setTimeout(() => {
      resolve(11);
    }, 300);
  });
Promise.all([fetch1, fetch2, fetch3]).then(res => {
  console.log(res);
})

// [33, 22, 11]

 

ES7 Async/await

这就是相比Promise更有用的地方、它允许你这样编写异步。

async function asynctask(cb){
        const userInfo = await userInfo();
        if(!userInfo) return cb('no userInfo found'); 
        const savedTask = await addList(userInfo);
        if(savedTask) return cb('add failure'); 
        cb(savedTasks);
}

 

  • Async返回的是个Promsie对象、可以像Promise一样使用then方法执行下一步操作。Async函数 可以看做多个异步操作、包装成的一个Promise对象、await命令就是内部then方法的语法糖。
  • await 操作符等于等待一个promise对象。它只能在异步函数async function中使用。所以正常情况下await后面是个promise对象。如果不是、await会把该值转换成已正常处理的promise、返回该值本身;

 

上面的代码看起是更清晰了,但是、错误处理呢?

由于异步函数在等待Promise、当Promise遇到错误时、它会抛出一个异常、该异常将在Promise的catch方法中捕获。

在async/await函数中,通常使用try/catch块来捕获此类错误。

 

const fetchData = () => {
  return new Promise( (resolve, reject) =>{
     settimeout(resolve(),300)
  } )
};

async function main() {
  try {
    const res = await fetchData();
  }
  catch(e) {
   console.log(e);
  }
};

 

 

 

 

 

延展阅读:

Promises/A+

how-to-write-async-await-without-try-catch-blocks-in-javascript

Promise 对象 - 阮一峰博客

ES6 系列之我们来聊聊 Async - 掘金