玩转原生Promise


几个小时前我对Promise一无所知,几个小时后已经可以快乐的写代码了.
如果再让我写异步代码,我会首推Promise.
可见Promise十分简单而且必要.

渊源:
前段时间在分析一款JS IM产品.由于对方使用了混淆.我一度分析到了Promise库中.
Promise是一个熟悉且陌生的单词,今天把它搞清楚.

玩转原生Promise

let pro = new Promise()

Promise的优势在于,可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作。
在then方法中,你也可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了

先说清楚 我们这里指的Promise 是ES6原生的.而不是 jQuery库自己实现的Promise.

疑问

  1. Promise到底是干啥的?只知道其貌似是处理异步的
  2. 怎么在then中 终止Promise?

Promise解决了三类任务需求

  • 延迟执行之后继续延迟执行(异步)
    在1s后执行一件事.并且在该事情执行后继续1s中执行一件事,eg:
    1s后自动加载一张图片,加载成功的话,就再加载一张图片.
  • 批量异步执行
    加载20张图片同时.
    .all_并发且等待全部执行完毕 例子
  • 监视类任务
    加载某图片,如果X秒后没有加载成功.提示加载不成功.
    .race_竞赛 例子

本小节举第一个例子:
1s后加载一张图片,再加载另一张图片

看一下不用Promise的传统写法:

setTimeout(() => {

    //加载第一张图片
    let image1 = new Image();
    image1.src = "https://static2.cnodejs.org/public/images/cnodejs_light.svg"


    //加载成功调用这里
    image1.onload = () => {
        //将图片到界面并显示出来
        console.log("第一张图片加载成功")
        document.body.appendChild(image1);

        setTimeout(() => {

            //加载第二张图片
            let image2 = new Image();
            image2.src = "https://profile.csdnimg.cn/B/1/4/2_dalerkd"
            image2.onload = () => {
                console.log("第二张图片加载成功")
                document.body.appendChild(image2);
            }

        },1000)
    }
},1000)

这种顺序性任务 显然变成了 越来越长的东东。。。。真心难受.
如果连续20个相互依赖的步骤的任务呢?
这种情况 被 JS人 称为 回调地狱.

其实我们工作中很多代码都是这种 有依赖性的 逻辑.eg:
检查密码是否为空->使用加密算法处理密码->发送给服务器并等待成功

是时候用原生Promise来做了:
以上代码可以写为:



function load_second_image(){
 //加载第二张图片
 let promise = new Promise((res,rej)=>{
     let image2 = new Image();
     image2.src = "https://profile.csdnimg.cn/B/1/4/2_dalerkd"
     image2.onload = () => {
            res('第二张图片加载成功')
            document.body.appendChild(image2);
     }
     setTimeout(()=>{
      rej('5秒内没有加载成功第2张图片,终止后续任务')
     },5000)
 })
 return promise;
}


function load_first_image(){
 let promise = new Promise((res,rej)=>{//我写了简称,毕竟只是函数而已嘛
        let image1 = new Image();
        image1.src = "https://static2.cnodejs.org/public/images/cnodejs_light.svg"
        //加载成功调用这里
        image1.onload = () =>{
         res('第一张图片加载成功')
         document.body.appendChild(image1);
     }
     setTimeout(()=>{
      rej('5秒内没有加载成功第1张图片,终止后续任务')
     },5000)
})
 return promise;
}


load_first_image().then((value)=>{
 console.log(value);
 return load_second_image();
    })
.then((value)=>{
 console.log(value)
})
.catch(err=>{
 console.log("失败:",err)
})


我们看到 可以在 .then中 直接返回 Promise.resolve或者 Promise.reject来立马决定成功或者失败.
也可以 返回new Promise.(例中就是这样,好处是不立马决定是成功还是失败)
还可以 直接 return 数据而不是Promise对象.eg: return '成功完成.'

then返回什么?

  • 返回Promise对象
  • Promise.resolve/Promise.reject
  • new Promise
  • 直接返回数据
    默认为resolve.

基础语法

var promise = new Promise((resolve, reject) => {
    // 异步处理
    // 处理结束后,调用 resolve 或 reject
    // 成功时就调用 resolve
    // 失败时就调用 reject
});

链式操作

表面上是简化了 层层回调. 实质上,Promise的精髓是 “状态”.
用 维护状态 和 传递状态 的方式来使得回调函数能够及时调用.

promise().then().then().catch()

如何在链式中 终止?

示例:

start()
  .then(data => {
    // promise start
    console.log('result of start: ', data);
    return Promise.resolve(1); // p1
    )
  .then(data => {
    // promise p1
    console.log('result of p1: ', data);
    return Promise.reject({
      notRealPromiseException: true,
    }); // p2
  })
  .then(data => {
    // promise p2
    console.log('result of p2: ', data);
    return Promise.resolve(3); // p3
  })
  .catch(ex => {
    console.log('ex: ', ex);
    if (ex.notRealPromiseException) {
      // 一切正常,只是通过 catch 方法来中止 promise chain
      // 也就是中止 promise p2 的执行
      return true;
    }
    // 真正发生异常
    return false;
  });

.then的例子

start(){
let p = new Promise(function(){
  setTimeout(()=>{return Promise.resolve('成功')})
 }
)
 return p;
}
.then(data=>{
 return Promise.resolve('成功');
}
)
.then(data=>{
 return Promise.reject('失败');//失败
}
)
.catch(err=>{
 console.log(err);
}
)

.all_并发且等待全部执行完毕

Promise的.all操作 传入 函数数组,能并发执行它们.
它之后的then会等大家都完成后才将 大家的参数全部拼成一个数组来返回给then.

.race_竞赛

类似.all只是,每完成一个Promise就立马用返回的参数调用then.
所以.then的参数不是数组在这里.
一种用法是:
将工作函数放在一个Promise中,将超时函数放在一个Promise中.
超时函数 在指定时间后 负责设置 reject.
由于设置成某个状态后,就不能改变了,所以就能在超时后直接设置状态.
catch 函数是用于处理失败情况的.

//请求某个图片资源
function requestImg(){
    var p = new Promise(function(resolve, reject){
        var img = new Image();
        img.onload = function(){
            resolve(img);
        }
        img.src = 'xxxxxx';
    });
    return p;
}

//延时函数,用于给请求计时
function timeout(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            reject('图片请求超时');
        }, 5000);
    });
    return p;
}

Promise
.race([requestImg(), timeout()])
.then(function(results){
    console.log(results);
})
.catch(function(reason){
    console.log(reason);
});

参考

文章主要参考:
大白话讲解Promise(一)
https://www.cnblogs.com/lvdabao/p/es6-promise-1.html

Promise 的链式调用与中止
https://cnodejs.org/topic/58385d4927d001d606ac197d

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值