angular promise请求_浅析JS中Promise

前言

查看之前的文章

目录

  • Promise 的用途
  • 如何创建一个 new Promise
  • 如何使用 Promise.prototype.then(可查 MDN)
  • 如何使用 Promise.all(可查 MDN)
  • 如何使用 Promise.race(可查 MDN)

以AJAX的封装为例来解释Promise的用法

拓展:析构赋值

cosnt success = options.success
cosnt fail= options.fail

等价于优化

cosnt {success,fail} = options
  • AJAX定义
ajax = (method,url,options) =>{
  cosnt {success,fail} = options
  const request = new XMLJttpRequest()
  request.open(method,url) //method:方法;url:地址
  request.onreadystatecharge = ()=>{
    if(request.readyState === 4){//readyState:准备状态 4为准备完成发送状态码(可能发送成功的也可能发送失败)的
      if(request.status<400){    //成功就调用success(小于400)
        success.call(null,request.response)
      }else if(request.status>=400){//失败就调用fail(大于等于400)
        fail.call(null,requset,request.status)
      }
    }
  }
  request.send()
}

AJAX使用

特地:使用 两种调用进行演示
ajax('get','/xxx',{
  success:(response){},    //成功:function缩写
  fail:(request,status)=>{}//失败:箭头函数 ES6的新语法!!!重要
})

Promise说这个代码太傻了

我们改用Promise写法

step1:先改调用姿势

//老版调用
ajax('get','/xxx',{
  success:(response){},    //成功:function缩写
  fail:(request,status)=>{}//失败:箭头函数 ES6的新语法!!!重要
})

//新版Promise写法调用
ajax('get','/xxx',{
  .then((response)=>{},(request,status)=>{})//因为失败只接受一个参数,所以status会被忽略,为无效参数
})
  • 虽然也是回调
  • 但是不需要记success 和 fail 了
  • then的第一个参数就是success
  • then的第二个参数就是fail

请问ajax()返回了啥?

返回了一个含有.then()方法的对象呗

那么在请问如何得到这个含有.then()的对象呢?

那就要改造ajax的源码了

step2:改造ajax源码

ajax = (method,url,options) =>{
  return new Promise ((resolve,reject)=>{ //一进来就return
    cosnt {success,fail} = options
    const request = new XMLJttpRequest()
    request.open(method,url) //method:方法;url:地址
    request.onreadystatecharge = ()=>{
      if(request.readyState === 4){//readyState:准备状态 4为准备完成发送状态码(可能发送成功的也可能发送失败)的
        if(request.status<400){    //成功就调用success(小于400)
          resolve.call(null,request.response) //决定(成功)
        }else if(request.status>=400){//失败就调用fail(大于等于400)
          reject.call(null,requset,request.status) //拒绝(失败)
        }
      }
    }
    request.send()
  })
}

Promise 的用途

Promise与异步关联

Promise 是一个对象,用于表示一个异步操作的最终完成 (或失败), 及其结果值.(MDN)

本质上Promise是一个函数返回的对象,我们可以在它上面绑定回调函数,这样我们就不需要在一开始把回调函数作为参数传入这个函数了。

02ea0943b29bf0dc6ca4b7954bf32694.png

案例代码:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('foo');
  }, 300);
});

promise1.then((value) => {
  console.log(value);
  // expected output: "foo"
});

console.log(promise1);
// expected output: [object Promise]

依次打印

> [object Promise]

> "foo"

通过以上代码可以看出Promise关于异步(setTimeout)的用法
因为设置了定时器,需要等待300ms后才会return value
异步并不需要等待(并不会阻塞代码),可以先往下执行,首先打印出 [object Promise]
等待300ms后setTimout返回value给Promise1,然后Promise1通过then函数打印出 foo

如何创建一个 new Promise

Promise 最重要的五个单词
return new Promise ((resolve,reject)=>{})

背过这五个单词即可,等你用熟了

小结

step1
  • return new Promise ((resolve,reject)=>{})
  • 任务成功则调用resolve(result)
  • 任务失败则调用reject(error)
  • resolve 和 reject 会再去调用成功和失败函数
step2
  • 使用.then(success,fail)传入成功和失败函数
  • 点到为止(先讲到这里,Promise还有高级用法,以后说)

我们封装的ajax的缺点

post无法上传数据

  • request.send(这里可以上传数据)

不能设置请求头

  • request.setRequestHeader(key,value)

怎么解决呢?

  • 花时间把ajax写到完美
  • 使用jQuery.ajax(这个可以)
  • 使用axios(这个库比jQuery逼格高)

jQuery.ajax

已经非常完美

  • 进入jQuery的文档,搜索ajax,找到jQuery.ajax
  • 看看参数说明,然后直接看代码示例
  • 看看jQuery的封装,就知道自己的封装辣鸡

封装优点

  • 支持更多形式的参数
  • 支持Promise
  • 支持的功能超多

我们需要掌握jQuery.ajax吗

  • 不用,现在的专业前端在用axios
  • 写篇博客罗列下功能即可

axios(重要)

目前最新的AJAX库
  • 显然他抄袭了jQuery的封装思路
  • 写篇博客(参考PDF方方博客)
axios.get('/5.json')  //地址:本地路径
  .then(response =>   //成功
   console.log(response)
)

axios高级用法

JSON自动处理
  • axios如发现响应的Content-Type是json
  • 就会自动调用JSON.parse
  • 所以说正确设置Content-Type是好习惯

请求拦截器

你可以在所有请求里加些东西,比如加查询参数

响应拦截器

你可以在所有响应里加些东西,甚至改内容

  • 可以生成不同实例(对象)

不同的实例可以设置不同的配置,用于复杂场景

封装!封装!封装!

  • 初级程序员学习API(包括Vue/React的API)
  • 中级程序员学习如何封装
  • 高级程序员造轮子

总结

  • 异步是什么
  • 异步为什么会用到回调
  • 回调有哪些三个问题:地狱,名字,错误处理
  • Promise是什么:1976年的一种设计模式
  • 如何使用Promise:背下来五个词
  • 如何使用Axios:发个请求试用看看
  • Promise是前端解决异步问题的统一方案(面试必考)
Promise​developer.mozilla.org
b2f8fef192e64eb46a0b27ea293c1c74.png
使用 Promise​developer.mozilla.org
b2f8fef192e64eb46a0b27ea293c1c74.png
Promise​developer.mozilla.org
b2f8fef192e64eb46a0b27ea293c1c74.png
彻底理解Javascript 中的 Promise(-------------------------------***---------------------------------)​www.cnblogs.com
c5c349340f2c1ebc2e8de0a5ff57bf03.png
大白话讲解Promise(一) - 吕大豹 - 博客园​www.cnblogs.com
329e5dc172861d329263d4ce05124297.png
Promise​www.liaoxuefeng.com
ed6b83fbfbf13cf606d1a0c789eb5b8a.png
Javascript 中的神器——Promise ​www.jianshu.com
266fd88adb2293aa142f1c631723cda1.png
JS中Promise的使用 - 个人文章 - SegmentFault 思否​segmentfault.com
d731dd186bea57b1ea711ee93e3d765f.png
人类身份验证 - SegmentFault​segmentfault.com JavaScript中的promise使用 - 个人文章 - SegmentFault 思否​segmentfault.com
d731dd186bea57b1ea711ee93e3d765f.png
JavaScript Promise 对象​www.runoob.com Promise​developer.mozilla.org
b2f8fef192e64eb46a0b27ea293c1c74.png
Promise​s0developer0mozilla0org.icopy.site
b2f8fef192e64eb46a0b27ea293c1c74.png
Promise对象 3 种妙用 - 掘金​juejin.im

降低耦合度方面

一个库依赖另一个库或者另外几个库难以避免。
但是依赖太多,就难以独立。
如果说,某些功能调用确实是存在调用其它功能的地方,可以利用回调函数、事件通知这些方式来降低耦合,不同的复杂度可以用不同的方法。
(如果封装的功能过于复杂,其中的事件通知部分甚至可以扩展到“消息总线”的机制)
比如说封装的时候:
① 某一功能调用很简单,可以直接出结果(像 1+1 = 2 这种),就直接使用返回值的方式。
② 如果某个功能需要有一定的时间进行处理,或者调用其它的功能(定义好参数、返回值),可以用回调的方式。
③ 如果涉及到多个状态的变化,或者多次调用,可以通过事件(或者消息总线)的方式。

这里提一点,如果回调的层次过多,有可能会陷入js中著名的回调地狱(callback hell)。
这是使用的回调时候需要特别注意的地方。
当然,我们也可以用 Promise 的机制来避免这一点。(Promise是 ES6 中引入的新语法,老的浏览器可以使用 jQuery 中的 $.Deferred 来替代,也有一些其他的封装)
其实,Promise机制同字面上的意思,就是先给你个承诺(返回值对象),当有结果的时候再给你(回调)。
比如说:
doSomething()
.then(doSomething_2())
.thne(doSOmething_3());
(注意:同样 Promise 用不好,也很坑;不管是回调,还是 Promise 都只是一个方法而已,关键是你怎么用)

如何使用 Promise.prototype.then

then是Promise实例对象的一个属性函数,返回的是一个Promise对象,有两个参数,一个是成功的回调函数,一个是失败的回调函数

then()方法返回一个Promise。它最多需要有两个参数:Promise 的成功和失败情况的回调函数。

const promise1 = new Promise((resolve, reject) => {
  resolve('Success!');
});

promise1.then((value) => {
  console.log(value);
  // expected output: "Success!"
});

使用 then 方法

var p1 = new Promise((resolve, reject) => {
  resolve('成功!');
  // or
  // reject(new Error("出错了!"));
});

p1.then(value => {
  console.log(value); // 成功!
}, reason => {
  console.error(reason); // 出错了!
});

如何使用 Promise.all

返回的是Promsie对象,参数可以是Promise实例对象的数组

Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。

具体代码如下:

let p1 = new Promise((resolve, reject) => {
  resolve('成功了')
})

let p2 = new Promise((resolve, reject) => {
  resolve('success')
})

let p3 = Promse.reject('失败')

Promise.all([p1, p2]).then((result) => {
  console.log(result)               //['成功了', 'success']
}).catch((error) => {
  console.log(error)
})

Promise.all([p1,p3,p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)      // 失败了,打出 '失败'
})

Promse.all在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示,在此之前只显示loading图标。

代码模拟:

let wake = (time) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${time / 1000}秒后醒来`)
    }, time)
  })
}

let p1 = wake(3000)
let p2 = wake(2000)

Promise.all([p1, p2]).then((result) => {
  console.log(result)       // [ '3秒后醒来', '2秒后醒来' ]
}).catch((error) => {
  console.log(error)
})

需要特别注意的是,Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。

在实际项目中,可能会遇到 需要从前两个接口中的返回结果获取第三个接口的请求参数这种情况。 也就是需要等待两个/多个异步事件完成后,再进行回调。
对于异步回调,首先想到的就会是使用Promise封装,然后使用.then()来触发回调。那么对于两个或多个异步事件均完成后再触发回调可以使用Promise.all()方法。**Promise.all(iterable)** 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。

Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败的原因是第一个失败 promise 的结果。

它通常在启动多个异步任务并发运行并为其结果创建承诺之后使用,以便人们可以等待所有任务完成。

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values);
});
// expected output: Array [3, 42, "foo"]
Promise.all用法举例_大道至简-CSDN博客​blog.csdn.net
5129209cda44a61ee1f5fa40166f7c91.png

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
 */

.可以看出只要有一个地址出错,那么所有的就都不会输出

var http = require("http");
 
// 
function getURL(url){
	return new Promise(function(resolve, reject){
		http.get(url, function(res){
			resolve(res);
		}).on("error", function(e){
			reject(e);
		});
	});
}
 
// 
var itbilu = getURL("http://itbilu.com"); 
var yijiebuyi = getURL("http://yijiebuyi111.com"); 
 
Promise.all([itbilu, yijiebuyi]).then(function(results){
	results.forEach(function(result){
		console.log(result.statusCode);
	});
}).catch(function(err){
	console.log(err);
});
 
/**
1)正常情况下输出:
301
200
2)故意拼错一个地址(itbilu拼错): 
var itbilu = getURL("http1://itbilu.com"); 
[Error: Protocol "http1:" not supported. Expected "http:".]
3.访问不存在的地址 
var yijiebuyi = getURL("http://yijiebuyi.com1"); 
{ [Error: getaddrinfo ENOTFOUND yijiebuyi.com1 yijiebuyi.com1:80]
  code: 'ENOTFOUND',
  errno: 'ENOTFOUND',
  syscall: 'getaddrinfo',
  hostname: 'yijiebuyi.com1',
  host: 'yijiebuyi.com1',
  port: 80 }
 */

如何使用 Promise.race

返回一个Promise对象,参数是Promise对象,或Promise数组

顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  },1000)
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('failed')
  }, 500)
})

Promise.race([p1, p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)  // 打开的是 'failed'
})

案例二:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});

Promise.race([promise1, promise2]).then((value) => {
  console.log(value);
  // Both resolve, but promise2 is faster
});
// expected output: "two

原理是挺简单的,但是在实际运用中还没有想到什么的使用场景会使用到。

人类身份验证 - SegmentFault​segmentfault.com 理解和使用Promise.all和Promise.race​www.jianshu.com
36056b47bb4361b8d227b7c1c0465430.png
理解和使用Promise.all和Promise.race​www.jianshu.com
36056b47bb4361b8d227b7c1c0465430.png
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值