Promise 快速入门学习笔记

由于JS单线程执行的这个“缺陷”,很多涉及到网络操作和浏览器事件都必须采用异步执行的方法。本人由于我的老师一直推崇RxJS,所以很多时候都是采用RxJS来处理异步事件。但是最近维护公司的老项目的时候,很多时候都是用Promise处理的,所以特意写这篇文章来记录一下我的学习路程,加深自己的理解和印象。(写完这个写RxJS的学习路程 ̄▽ ̄)


1.为什么要使用Promise

Promise通过链式调用方法的形式,用写同步代码的形式去处理异步操作。(这点感觉是JS比较流行的异步编程解决方法的共同特点了,另如RxJSAxios等)

Example:

// 使用Promise前
$.get(params1, data1 => {
    console.log(data1);
    $.get(data1, data2 => {
        console.log(data2);
    })
})
复制代码
// 使用Promise后
const get$ = (params) => {
    return new Promise((resolve, reject) => {
        $.get(params, data => {
            resolve(data);
        })
    })
}

get$(params)
.then(data1 => {
    return get$(data1);   
}).then(data2 => {
    return get$(data2);
}).then(data3 => {
    console.log(data3);
})
.catch(err => throw new Error(err));
复制代码

哈哈是不是好理解多了呢!(并不)

既然知道了我们为什么要用Promise,接下来就看看我们要怎么用。

2.Promise是什么?。?

要用一个东西,首先就要理解它是什么。个人比较习惯从源码层面方面去理解一个功能。 业界生产Promise都遵循Promise/A+标准,传送门:Promise/A+

下面是掘金里几篇我觉得写得不错的Promise源码解析和实现的文章,有兴趣的同鞋可以去看一下:

3.Promise一些基础用法和特性

3.1 生成一个Promise对象

const promise = new Promise(resolve, reject) => {
    resolve(successData); //'执行成功发出的值'
    rejecet(errReason); //'执行失败发错的错误信息'
})

promise.then((successData) => { /*处理成功值*/})
    .catch((errReason) => {/*处理错误信息/});
复制代码

一个快速得到Promise对象的写法: Promise.resolve(params), Promise.reject(reason)

Promise.resolve(params);
Promise.reject(reason);
等价于
new Promise((resolve, reject) => {
    resolve(params);
    reject(reason);
});
复制代码

当然这个方法根据传入的params的类型不同会有不同的表现,想要详细了解避免踩坑的可以看这里:Promise.resolve(), Promise.reject()

若要实现多重回调,只要像第一段里的Example那样,在前一个then中return 一个新的Promise实例就行。当回调链中有reject的Promise时中断回调链执行catch回调。 当前一个then中假如没有return,那么下一个then中接受到值是undefined。

get$(params)
.then(data1 => {
    return get$(data1);   
}).then(data2 => {
    return get$(data2);
}).then(data3 => {
    console.log(data3);
})
.catch(err => throw new Error(err));
复制代码

3.2 Promise的一些方法

catch, then就不用说了,就是基本用法。
finally不管Promise resolve还是reject之后,总会在then和catch执行之后执行。

Promise.all(:Promise[])

all方法接受一个Promise实例数组作为参数,创建一个新的Promise实例。
该实例当参数内所有的Promise 都resolve之后或参数中不包含 Promise时resolve。
当参数内有一个或以上的Promise reject之后,该实例reject。

let Promise1 = new Promise(function(resolve, reject){ resolve(v1); reject(r1); })
let Promise2 = new Promise(function(resolve, reject){ resolve(v2); reject(r2); })
let Promise3 = new Promise(function(resolve, reject){ resolve(v3); reject(r3); })

let p = Promise.all([Promise1, Promise2, Promise3])
p.then(([v1, v2, v3]) => {
  // 三个都成功则成功, 参数为三个Promise成功值构成的数组
}, (r2) => {
  // 只要有失败,则失败, 假设Promise2失败
})
复制代码

Promise.race(:Promise[])

race方法接受一个Promise实例数组作为参数,创建一个新的Promise实例。
数组中的的Promise对象为竞争关系, 当参数内有一个或以上的Promise resolve后,该实例resolve。 当参数内有一个或以上的Promise reject之后,该实例reject。

let Promise1 = new Promise(function(resolve, reject){ resolve(v1); reject(r1); })
let Promise2 = new Promise(function(resolve, reject){ resolve(v2); reject(r2); })
let Promise3 = new Promise(function(resolve, reject){ resolve(v3); reject(r3); })

let p = Promise.all([Promise1, Promise2, Promise3])
 // 假设Promise1, Promise2 resolve,Promise3 reject,且Promise2 resolve最快
p.then((v2) => {
 // v2 比 v1 快, 但因为Promise3 reject了,并不会执行此处回调
}, (r3) => {
  // 返回第一个reject的reason
})
复制代码

3.3 Promise的进阶方法

在学习Promise的过程中我看到一篇文章,里面提到了很多Promsie的进阶函数,诸如map,reduce,join等,在我看来跟数组的同名方法行为是一致的,都是对数组的遍历操作,并额外添加对数组遍历的结果添加then和catch方法。简而意之就是快速生成多个Promise对象并添加回调函数。
有兴趣的同鞋到下面链接去看看,我理解有什么不对的地方可以及时指正我,毕竟我对这些方法也只是一知半解。 Javascript 中的神器——Promise

4.Promise应用实例:

下面都是一些我在项目中主要使用到Promise的地方,大家可以看一下,有什么想法都可以跟我交流~

4.1 封装一个基于Promise的网络请求方法:

function httpFetch(url, params) {
    // 如果已经出现了错误,则取消掉请求并抛出错误
    if(ErrorStatus) {
        return new Promsie((resolve, reject) => {
            const error = new Error();
            error.status = 400 // 根据所需的情况返回状态码
            reject(error);
        }
    }
    
    // 执行网络请求, 并处理返回的结果
    return runFetch(url, params).then((response) => {
        handleResponse(response, url, params);
    });
}
复制代码
// 处理url和params
function prepareFetchData(url, params) {
    const method = params.method.toLocaleUpperCase();
    let fetchUrl = url;
    let options = {};
    if(params.data) {
        /* HTTP规范没有明确禁止DELETE请求的body,
        但是很多服务器的实现会忽略它,所以我们将DELETE和GET请求通过
        url query传递数据*/
        if(method !== 'GET' && method !== 'DELETE') {
            // 这里可能需要预先处理数据,使它符合网络规范
            options.body = preTreatData(params.data);
            // 或者使用 XMLHttpRequest 与服务器交互
            options.xhrData = preTreatData(params.data);
            // 上述两种可二选一
        } else {
            // 通过query传递数据,需预处理data
            const query = convertDataToQuery(params.data);
            if (query.length) {
                fetchUrl = fetchUrl + (fetchUrl.include('?') ? '&' : '?') + query.join('&');
            }
        }
    }
    return { url, options },
}

// 与服务器交互获取数据
function runFetch(url, params) {
    const paramsData = prepareFetchData(url, params);
    // 根据情况选用不同的与服务器交互方法
    /* 使用xhrRequest交互, xhrRequest具体实现自己去研究吧~
    主要使用到XMLHttpRequest对象的open,onload,onerror,send方法*/
    
    xhrRequest(fetchData.url, fetchData.options)
        .then((data) => {
          resolve(data);
        }, (err) => {
          reject(err);
        });
    
    // 使用web fetch api, 也是一个Promise对象
    fetch(paramsData.url, paramsData.options).then((response) => {
        response.text().then((text) => { // Promise again
          try {
            resolve({
              data: JSON.parse(text),
              status: response.status,
            });
          } catch (e) {
            resolve({
              data: text,
              status: response.status,
            });
          }
        }, (err) => {
          reject(err);
        });
      }).catch((error) => {
        reject(error);
      });
}
复制代码
// 处理请求返回的结果
function handleResponse(response, url, params) {
    retrun new Promise((resolve, reject) => {
        // 对不同状态码进行特定处理
        if (response.status >=200 && response.status < 300) {
            resolve(response.data);
        }
        const error = new Error();
        error.status = response.status;
        error.data = response.data;
        
        /* 此处可以添加对错误状态码的处理:
        如401, 403, 500等*/
    
        reject(error);
    });
}
复制代码
// 生成网络请求并使用(例)
function getResponse(data) {
    return httpFetch('/api/url', {
      // 'GET', 'POST', 'DELETE' 等
      method: 'GET',
      data,
    })
}

复制代码

这样我们就用Promise实现了一个通用的httpFetch方法,可以添加全局错误处理等~

(后续有在项目中用到的话再添加更多应用例子吧......)

5. 一些替代Promise的写法

5.1 async/await

第一段中我们提到的Promise写法可用es7的语法糖async/await替代,让它看起来更像同步代码,语法更加简洁。

// 使用async await 替代 Promise

const asyncFunction = async (params) => {
    try {
        const data1 = await get$(params);
        const data2 = await get$(data1);
        const data3 = await get$(data2);
        console.log(data3);
    } catch (error) {
        conosle.log(error);
    }
}
复制代码

6.拓展阅读

当然这篇文章只是对Promise点到为止,我推荐大家去阮一峰老师的es6专栏中详细的学习一下。 阮一峰es6入门-Promise

下面是涉及到Promise的一些特性和奇奇怪怪的坑的文章,看完之后感觉对Promise理解更深了,也推荐大家去看一下,毕竟开卷有益嘛

好啦这篇没有什么营养的文章到此就结束啦 ~ 接着我会详细写几篇关于自己学习RxJS的文章(毕竟自己最常用的是这个),希望能够帮助到大家学习啦!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值