【js】Promise完整解析以及async/await和Promise.all

Pre:

无论是Promise还是async/await的目的,都是为了让异步的操作更像同步

一、why?Promise解决的问题

当一个异步任务的执行需要依赖另一个异步任务的结果时,我们一般会将两个异步任务嵌套起来,这种情况发生一两次还可以忍,但是发生很多次之后,你的代码就会变成这个熊样:

    async1(function(){

        async2(function(){

            async3(function(

                async4(funciton(){

                    async5(function(){

                        //...

                    });

                });

            ));

        });

    });

这就是所谓的回调地狱,代码层层嵌套,环环相扣,很明显,逻辑稍微复杂一些,这样的程序就会变得难以维护。

在ES2015的标准里,Promise的标准化,一定程度上解决了js的流程操作问题

 

二、what?

Promise对象有三种状态,他们分别是:

  • pending:等待中或者进行中,表示还没得到结果

  • resolve(fulfilled):已经完成,表示得到了我们想要的结果,可以继续往下执行

  • rejected:也表示得到结果,但是由于结果并非我们希望的,因此拒绝执行

     

    这三种状态不受外界影响,而且状态只能从pending改变为resolved或者rejected,并且不可逆。

       在Promise对象的构造函数中,接受一个函数作为参数,该函数就是用来处理Promise的状态变化的。

       该函数接受两个额外的函数resolve和reject;

       这两个函数分别代表将当前Promise置为fulfilled(解决)和rejected(拒绝)两个状态;和用来存值

var _promise = new Promise(function(resolve, reject){

  setTimeout(function(){

    var rand = Math.random();

    if(rand<0.5){

        resolve("resolve"+rand);

    }else{

        reject("reject"+rand);

    }

  }, 1000)

})

 

三、how?

 

(1)实例进行异步操作存值,然后在then中展开具体操作

Promise使用的正确姿势是在其外层再包裹一层函数。如下:

var run = funciton(){

  return new Promise(function(resolve, reject){

    setTimeout(function(){

      var rand = Math.random();

      if(rand<0.5){

          resolve("resolve"+rand);

      }else{

          reject("reject"+rand);

      }

    },1000)

  })

}

run();

这是Promise的正常用法,这样才会在想要使用它的时候才调用创建。最后的返回值是一个promise

一个Promise必须提供一个then来把它resolve和reject保存的内容展开利用

then方法可以接受两个回调函数作为参数

promise.then(

onFulfilled:成功回调, 

onRejected:失败回调可选);

接着上面创建的函数run(),我们来进行对异步操作结果的处理:

run().then(

//处理resolve的代码

function(data){

  console.log("Promise被置为resolve",data);

},

//处理reject的代码

function(data){

  console.log("Promise被置为reject",data);

});

 

对reject后的处理,除了可以在.then 的第二个参数中处理,还可以在 .catch 方法中设置想要调用的函数。

推荐使用这种,更清晰!

run().then(function(data){

  //处理resolve的代码

  console.log("Promise被置为resolve",data);

}).catch(function(data){

  //处理reject的代码

  console.log("Promise被置为reject",data);

})

这样,一个次完整的Promise调用就结束了。

 

(2)then的链式操作,依次执行,数据依赖

 

对于Promise的then()方法,then总是会返回一个Promise实例:

promise2 = promise1.then(onFulfilled, onRejected);

因此我们可以像上面面那样接着对其返回值进行 .then 调用,形如run().then().then().then().then().then().....

在一个then()方法调用异步处理成功的状态时。

你既可以return一个确定的“值”;

也可以再次返回一个Promise实例;

当返回的是一个确切的值的时候,then会将这个确切的值传入一个默认的Promise实例!

并且这个Promise实例会立即置为fulfilled状态,以供接下来的then方法里使用。如下所示:

    run().then(function(data){

        console.log("第一次",data);

        return data;//返回一个确切的值,这个值也会被传入一个默认Promise存起来

    }).then(function(data){

        console.log("第二次",data);

        return data;

    }).then(function(data){

        console.log("第三次",data);

        return data;

    });

    /* 异步处理成功的打印结果:

        第一次 resolve0.49040459200760167d.js:18

        第二次 resolve0.49040459200760167d.js:21

        第三次 resolve0.49040459200760167

        由此可知then方法可以无限调用下去。

    */

根据这个特性,我们就可以将相互依赖的多个异步逻辑,进行顺序管理。

下面举一个完整的例子:

//第一个异步任务

function run_a(){

return new Promise(function(resolve,reject){

      //假设已经进行了异步操作,并获得了数据

      resolve("step1");

  })

}

//第二个异步任务

function run_b(){

return new Promise(function(resolve,reject){

      //假设已经进行了异步操作,并获得了数据

      resolve("step2");

  })

}

//第三个异步任务

function run_c(){

return new Promise(function(resolve,reject){

      //假设已经进行了异步操作,并获得了数据

      resolve("step3");

  })

}

 

//连续调用

run_a().then(function(data){

  //返回一个Promise实例

  return run_b(data);

}).then(function(data){

  return run_c(data);

}).then(function(data){

  console.log(data);

})

 四、async/await

 

 (1)async

函数前面的async一词意味着一个简单的事情:这个函数总是返回一个promise,

如果代码中有return <非promise>语句,

JavaScript会自动把返回的这个value值包装成promise的resolved值。

async function f() {

    // return Promise.resolve(1) == return 1;

    return 1;

};

f().then((data)=>{

    console.log(data);

}); // 1

(2)await

await可以让JavaScript进行等待,直到一个promise执行并返回它的结果,取代需要展开的then,

PS:aswit的使用,必须搭配async

async function h(){

   //构造函数实现

    let promise = new Promise((resolve, reject) => {

        setTimeout(   () => resolve(3)  ,  1000  )

    })

    let result = await promise // 直到promise做异步操作,但是一定要有resolve()和reject()

    console.log(result);

};

h();

//也可以直接抛出错误

async function i(){

    var value = await Promise.reject(new Error('whoops!'));   //等价于  throw new Error('Whoops!')

    console.log(value);

}

i();

常用用法:

//在model.js里

run(){

     return new Promise((resolve,reject)=>{

           resolve()   or reject()

           ...

     })

}

//在main.js

let main = async (ctx) =>{

    let data = await model.run();// 异步转同步

    //用data去做其他操作做其他操作

};

 

 

五、Promise.all

1.并发执行

var p1 = Promise.resolve(1);
var p2 = Promise.resolve(2);
var p3 = Promise.resolve(3);
 
Promise.all([p1, p2, p3])

.then(function(results){
    console.log(results);
});
 
/**
 [ 1, 2, 3 ]
 */

2.并发执行,出错一个,将会触发catch,并且只抛出报错的那一个

var p1 = Promise.resolve(1);
var p2 = Promise.reject(2); //失败的原因,catch将会捕获这个值
var p3 = Promise.resolve(3);
 
Promise.all([p1, p2, p3])

.then(function(results){
    console.log(results);
}).catch(function(e){
    console.log(JSON.stringify(e));
});
 
/**
 2
 */
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值