在了解Promise之前,我们来增加一些基础知识。
在前端中,异步是JS的重要特点,可以说没有异步处理,就没有前后端分离这一说。固然异步处理有异步处理的好处,比如:防止单线程阻塞的问题等。但是异步也会给我们带来一些问题。所以这个时候就需要异步的同步化。问题来了,请问异步同步化的方法有几种?
接下来,我们来聊聊什么Promise,以及Promise是如何处理异步同步化问题的。
为了解决异步问题,在ES6中加入了一个新的对象Promise。Promise提供了一种更合理、更强大的异步解决方案。告别回调地狱。
用于存放未来某个时间才会结束的操作。可以获取异步操作的消息。
Q1: 没有异步就不需要promise?
Q2: promise本身不是异步,只是编写异步的一种容器?
promise 是ES6 主要处理异步的方法,可以将异步操作同步化,安装一定的顺序去执行,符合我们需求的效果。
目的主要是为了防止页面冻结
之前的处理的异步同步化的问题基本上都是使用回调函数,但是很容易进入回调地狱并且代码难维护
注意:promise 是一个对象而不是一个函数的原因是: 对象可以保存状态二函数不行,同时也不会剥夺return的能力。
- 首先,无法取消
Promise
,一旦新建它就会立即执行,无法中途取消。- 其次,如果不设置回调函数,
Promise
内部抛出的错误,不会反应到外部。- 第三,当处于
pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
下面的例子说明以下区别:
$.ajax({
url: '/index',
type: 'get',
dataType: 'json',
success: function (data1) {
$.ajax({
url: '/home',
type: 'get',
dataType: 'json',
data: data1,
success: function (data2) {
$.ajax({
url: '/about',
type: 'get',
dataType: 'json',
data: data2,
success: function (data3) {
$.ajax({
url: '/me',
type: 'get',
dataType: 'json',
data: data3,
success: function (data4) {
console.log(data4)
}
})
}
})
}
})
}
})
new Pormise(resolve = > {
$.ajax({
url: '/index',
type: 'get',
dataType: 'json',
success:function (data1) {
resolve(data1)
}
})
})
.then(data1 => {
$.ajax({
url: '/home',
type: 'get',
dataType: 'json',
data: data1,
success: function (data2) {
resolve(data2)
}
})
})
.then(data2 => {
$.ajax({
url: '/about',
type: 'get',
dataType: 'json',
data: data2,
success: function (data3) {
resolve(data3);
}
)}
})
.then(data3 => {
$.ajax({
url: '/me',
type: 'get',
dataType: 'json',
data: data3,
success: function (data4) {
console.log(data4)
}
})
})
pending : 等待状态
比如正在网络请求,或定时器没有到时间, 可以迁移到执行态或拒绝态
fulfill : 成功状态
当我们主动调用了resolve时, 就处于该状态,并且会回调then函数,
不能迁移到其他任何的状态,必须拥有一个不可变的终值
reject : 失败状态
当我们主动调用reject时候,就处于该状态, 并且回调catch函数
不能迁移至其他任何状态, 必须拥有一个不可变的拒因
注意:当promise状态发生改变,就会触发then()里的响应函数处理后续步骤;promise状态一经改变,不会再变。
针对3种状态,只有两种转换的方向:
- pending -> fulfill 成功
- pending -> reject 失败
在状态装换的时候,就会触发事件:
- pending -> fulfill 触发 onFulFilled事件(resolve函数)
- pending -> reject 触发 onRejected 事件(reject函数)
在调用resolve 方法或者reject 方法的时候,就一定会触发事件
需要注册 onFulFilled 事件 和 onRejected 事件
针对事件的注册,提供了then 方法, 如下:
promise.then(onFulFilled, onRejected)
注: 有异步操作的结果,可以决定当前是哪一种状态,任何其他的操作都无法改变这个状态, 一定是状态的变化才能触发事件
// () => {} 叫执行器, 会立即执行
// 创建Promise,会默认的处于Pending状态,执行器会立即执行
//promise函数中的事件处理完成之后做了两件事:
// 1. 改变状态 pending -> resolve/reject 并且调用对应的回调函数
// 2. 将执行的结果传递给回调函数作为参数
let p = new Promise(() => {});
console.log(p); // pending
// 在执行器中需要传入两个参数 分别为resolve 和 reject,
// pending=>fulfilled(成功) pending=>rejected(失败)
let p = new Promise((resolve, reject) => {
resolve("有钱了");
reject("没钱了"); // 不执行
})
p.then(() => {
console.log('成功了...');
},() => {
console.log('失败了...');
})
console.log(p);
函数catch 是Promise中的一个方法,它会在Promise处于reject状态时调用触发
- 每个.then 返回一个独立的promise对象
- 每个return的返回值被下一个处理函数的参数接受
- 支持链式调用
- then() 执行 可以接受new promise 中异步执行结束的信号。
new Promise((resolve, reject) => {
setTimeout(() => {
//在Promise调用reject函数,会使Promise变为 reject 状态
//reject函数可以传入一个参数,作为catch函数的默认传入参数
resolve('成功'); // 调用reolve时,可以把成功的结果传递下去
reject('失败'); // 调用reject时,可以把失败的原因传递下去 ,不执行
},100)
// then 方法中两个参数
// 第一个参数表示从等待状到成功态,会调用第1个参数
// 第二个参数表示从等待状到失败态,会调用第2个参数
}).then(res =>{
console.log('有钱了...,因为' + res);
}, res =>{
console.log('没钱了...' + res);
})
// 处理失败的对象可以调用catch捕捉,同样,也可以通过throw错误对象 抛出一个错误对象,即失败
// throw 语句用于抛出一个用于抛出一个用户自定义的异常,当前函数的执行将被停止(throw之后的语句将不会执行),并且控制将被传递到调用堆栈中的第一个catch块,如果调用者函数没有catch块,程序将会终止。
function getRectArea(width, height) {
if(isNaN(width) || isNaN(height)) {
throw "Parameter is not a number!"
}
}
try {
getRectArea(3, 'A');
}
catch(e) {
console.log(e);
}
//.catch(err => {
// console.lgo(err);
//})
// 结果输出: 有钱了...因为成功
函数finally是Promise中的一个方法,它会在Promise 的最后触发, 无论Promise处于什么状态。
new Promise((resolve, reject) => {
setIimeout (() => {
resolve('成功了');
}, 1000)
})
.then(data => {
console.log(data);
})
/*
.catch(err => {
console.log(err);
})
*/
.finally(() => {
consoel.log('Promise结束');
})
/*结果输出: 成功啦
Promise结束
*/
函数all() 是Promise() 中的一个方法, 他用于将多个Promise实例,包装成一个新的Promise实例
一假为假
Promise.all([new Promise((resolve, reject) => {
setTimeout(() => {
//等待9个异步任务都成功之后, 提示上传成功; 所有都成功才执行才会去改变状态并且去触发.then
for(let i = 0; i < 9; i++){
console.log('异步程序',i);
resolve(i);
}
},3000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是第二个异步请求返回的数据')
},1000)
})
])
.then(results => {
console.log(results)
})
// 适合案例,并发业务,同时发送多个请求。
// ['我是第一个异步请求返回的数据', '我是第二个异步请求返回的数据']
将多个任务包含在Promise实例中, 只有有一个状态发生改变,就是改变Promise实例的状态。触发.then。
只要多个实例之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数
检测第一个实例的状态为整个实例的状态并返回给.then
promise 本身是同步的
console.log('start');
let p = new Promise(() => {
console.log('haha');
})
console.log('end');
// start haha end
但是then方法中的代码只有调用resolve, reject时候才会执行
let p = new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("setTimeout")
// resolve("有钱了...")
reject("没钱了...")
},1000)
})
p.then((suc)=>{
console.log(suc) // 有钱了...
},(err)=>{
console.log(err) // 没钱了...
})
// 运行结果: setTimeout 没钱了
注意:调用resolve
或reject
并不会终结 Promise 的参数函数的执行。这是因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。 所以有个特点是: 本轮的回调函数不会在本轮的事件循环中执行结束。
在链式调用.then中,只要存在一个失败,或错误,就会触发。捕获到错误。
该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。只要参数实例有一个变成fulfilled
状态,包装实例就会变成fulfilled
状态;如果所有参数实例都变成rejected
状态,包装实例就会变成rejected
状态。
一真为真
Q1: Promise.all 和 Promise.race的区别?
Q2: Promise.any 和 Promise.race的区别?
Promise.any()
跟Promise.race()
方法很像,只有一点不同,就是不会因为某个 Promise 变成rejected
状态而结束。
阅读Promise 编写的代码
Step1: 看then触发是哪一个Promise
Step2: promise 是怎么产生的?
结论: then 的回参 是如何传递的了?
Q1: 产生Promise的方式?
let p1 = Promise.resolve('1');
console.log(p1); //Promise对象
let p2 = Promise.all([1, 2]);
console.log(p2); //Promise对象
let p3 = Promise.race([1, 2]);
console.log(p3); //Promise对象
let p4 = new Promise((resolve) => {resolve('4') })
console.log(p4);
let p5 = p4.then((res) => { return res })
console.log(p5);
// 注意所有的Promise 都可以触发 .then回调函数
// 1. Promise.resolve(data) 中 then 的回参是 resolve 的实参data
p1.then(res => {
console.log(res); //1
})
// 2. all 是所有成功promise 的 resolve()实参
p2.then(res => {
console.log(res); //[1,2]
})
// 3. race 是第一个变为fulfilled状态的 promise 的 resolve() 实参
p3.then(res => {
console.log(res);//[1,2]
})
// 4. resolve() 实参
p4.then(res => {
console.log(res); //4
})
// 5. then 中的 return 返回值
p5.then(res => {
console.log(res); // 4
})
接下来,我们使用promise来模拟一下ajax来请求和发送数据。
function $ajax({ url = '/', method = 'GET', data = {} }) {
return new Promise((resolve, reject) => {
var params = '';
for (var x in data) {
params += `${x}=${data[x]}&`
}
params = params.slice(0, params.length - 1);
method = method.toUpperCase();
var xhr = new XMLHttpRequest();
if (method === 'GET') {
url = url + '?' + params;
xhr.open(method, url, true);
xhr.send();
}
if (method === 'POST') {
xhr.open(method, url, true)
xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
xhr.send(params)
}
xhr.onreadystatechange = function (data) {
setTimeout(function () {
if (xhr.readyState === 4 && xhr.status == 200) {
resolve(xhr.responseText)
} else {
reject(data)
}
}, 1000)
}
})
}
第一种方式:链式调用
<script>
function getDataView(query) {
$ajax({ url: 'http://localhost:3000/', data: query }).then(api => {
console.log(api, 'api');
return api
}).then(store=>{
console.log(store,'store');
return store
}).then(view=>{
console.log(view,'html');
})
}
getDataView({aa:11}) //执行
script>
第二种方式: 函数的方式
<script>
function getData(query){
return $ajax({ url: 'http://localhost:3000/', data: query }).then(api => {
console.log(api, 'api');
return api
})
}
function getDataStore(query){
return getData(query).then(store=>{
console.log(store,'store');
return store
})
}
function getDataView(query) {
getDataStore(query).then(view=>{
console.log(view,'html');
})
}
getDataView({aa:11})
script>
第三种方式: 文件拆分的方式
// index.html
function getDataView() {
console.log('请求已经成功接受了');
return getDataStore({aa:11}).then(res => {
console.log(res,'html');
})
}
getDataView(); //执行
//store.js
function getDataStore(query) {
return getData(query).then(store => {
console.log('getDataStore',store);
return store
})
}
//api.js
function getData(query) {
return new Promise((resolve, reject) => {
// resolve('getData');
return $ajax({ url: 'http://localhost:3000/', data: query }).then(api => {
console.log('getData', api);
resolve(api);
}).then(res => {
resolve(res);
})
})
// 返回一个.then 返回的Promise对象
}
//api 发请求,不处理结果。将结果传递出去
//store 接受响应结果,并将结果传递出去
//index 接受store 输出结果(响应结果),并处理结果(与视图相互结合)
最后贴上serve.js代码
const http = require('http');
const serve = http.createServer((req, res) => {
console.log('111');
res.writeHead(200, {
'Access-Control-Allow-Origin': '*',
"Access-Control-Allow-Methods": "*",
"Access-Control-Allow-Headers": "Content-Type,XFILENAME,XFILECATEGORY,XFILESIZE,X-URL-PATH,x-access-token"
})
res.end('hellow word')
})
serve.listen(3000)