应用场景
处理异步场景
如何理解Promise?
Promise不是消除回调,而是让回调变得可控。
如何使用?
const pro = new Promise((resolve, reject) => {
// 未决阶段
// 通过调用resolve函数,将Promise推向已决状态的resolved状态
// 通过调用reject函数,将Promise推向已决状态的rejected状态
// resolve和reject均可传递最多一个参数,表示推向状态的数据
})
举例说明:垦哥向女神发送表白短信 ES5版
// 垦哥向女神发送表白短信
/**
*
* @param {*} god 女神名
* @param {*} callback 有回应之后要执行的回调函数
*/
function biaobai(god, callback) {
console.log(`垦哥向女神${god}发送了表白短信`);
setTimeout(() => {
if (Math.random() < 0.1) {
// 女神同意啦
callback(true);
} else {
// 女神拒绝啦
callback(false);
}
}, 1000)
}
Promise版
const pro = new Promise((resolve, reject) => {
console.log(`垦哥向女神1发送了表白短信`);
setTimeout(() => {
if (Math.random() < 0.1) {
// 女神同意啦
resolve(true); // 通过resolve函数推向已决状态
} else {
// 女神拒绝啦
resolve(false); // 通过resolve函数推向已决状态
}
}, 1000)
})
举例说明:发送ajax请求
Promise版
const pro = new Promise((resolve, reject) => {
ajax({
url:'http://abc.json',
success: function(data){
resolve(data); // 请求成功之后推向已决阶段的已解决状态
},
error: function(err){ // 请求失败之后推向已决阶段的已拒绝状态
reject(err);
}
})
})
完整用法:
const pro = new Promise((resolve, reject) => {
// 未决阶段
// 通过调用resolve函数,将Promise推向已决状态的resolved状态
// 通过调用reject函数,将Promise推向已决状态的rejected状态
// resolve和reject均可传递最多一个参数,表示推向状态的数据
})
/**
* 后续处理
* 传两个参数(两个函数)
*/
pro.then(data => {
// 这是thenable函数,如果当前的Promise已经是resolved状态,该函数会立即执行
// 如果当前是未决阶段,则会加入到作业队列,等待到达resolved状态后执行
// data为状态数据
}, err => {
// 这是catchable函数,如果当前的Promise已经是rejected状态,该函数会立即执行
// 如果当前是未决阶段,则会加入到作业队列,等待到达rejected状态后执行
// err为状态数据
})
举例:
const pro = new Promise((resolve, reject) => {
console.log("未决阶段")
resolve(123);
})
pro.then(data => {
// 此时pro的状态是已决状态的resolved
console.log(data);
})
// 输出 未决阶段 123
const pro = new Promise((resolve, reject) => {
console.log("未决阶段");
setTimeout(() => {
if (Math.random() < 0.5) {
resolve(123);
} else {
reject(new Error('报错啦'));
}
}, 3000)
})
pro.then(data => {
// 此时pro的状态是已决状态的resolved
console.log(data);
}, err => {
console.log(err);
})
如何注册多个thenable?
pro.then(data => {
// 此时pro的状态是已决状态的resolved
console.log(data);
})
pro.then(data => {
// 此时pro的状态是已决状态的resolved
console.log(data);
})
pro.then(data => {
// 此时pro的状态是已决状态的resolved
console.log(data);
})
如何封装Promise?
-
举例:垦哥给女生翠花发送表白短信
function biaobai(god) { return new Promise((resolve, reject) => { console.log(`垦哥向女神${god}发送了表白短信`); setTimeout(() => { if (Math.random() < 0.1) { // 女神同意啦 resolve(true); } else { // 女神拒绝啦 resolve(false); } }, 2000) }) } biaobai('翠花').then(result => { console.log(result); })说明:
- 后续处理全是通过P romise,不需要再传回调函数。
- 没有消除回调,只是把回调变得可控。
-
举例:封装Ajax
function ajax(obj) { return new Promise((resolve, reject) => { //指定提交方式的默认值 obj.type = obj.type || "get"; //设置是否异步,默认为true(异步) obj.async = obj.async || true; //设置数据的默认值 obj.data = obj.data || null; // 根据不同的浏览器创建XHR对象 let xhr = null; if (window.XMLHttpRequest) { // 非IE浏览器 xhr = new XMLHttpRequest(); } else { // IE浏览器 xhr = new ActiveXObject("Microsoft.XMLHTTP"); } // 区分get和post,发送HTTP请求 if (obj.type === "post") { xhr.open(obj.type, obj.url, obj.async); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); let data = toData(obj.data); xhr.send(data); } else { let url = obj.url + "?" + toData(obj.data); xhr.open(obj.type, url, obj.async); xhr.send(); } // 接收返回过来的数据 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) { resolve(JSON.parse(xhr.responseText)) } else { reject(xhr.status) } } } }) } // 调用ajax函数 ajax({ url: 'http://abc.json' }).then(resp => { console.log(resp); }, err => { console.log(err); })
细节
-
未决阶段的处理函数是同步的,会立即执行
const pro = new Promise((resolve, reject) => { console.log("a"); }) console.log("b"); // 输出 a b -
thenable和catchable函数是异步的,就算是立即执行,也会加入到事件队列中等待执行,并且,加入的是微队列
举例说明:
const pro = new Promise((resolve, reject) => { console.log("a"); resolve(1); console.log("b"); }) pro.then(data => { console.log(data) }) console.log("c"); // 输出 a b c 1- thenable和catchable函数是异步的,所以会把这些函数加入到事件队列
- 同步函数执行完,再执行事件队列里的函数
- 注册then的时候已经是resolved了,就算是resolved了,立即执行了,但由于她是异步函数,它也要加入到事件队列,只不过是立即加入到事件队列而已
举例说明:
const pro = new Promise((resolve, reject) => { console.log("a"); resolve(1); setTimeout(() => { console.log("b"); }, 0); }) pro.then(data => { console.log(data) }) console.log("c"); // 输出 a c 1 b- 首先创建Promise,马上执行同步代码,输出a;
- 然后状态推向resolved;
console.log("b")加入到宏队列;- 注册then事件,promise已经是resolved状态,所以
console.log(data)立即推向微队列; - 同步代码输出c;
- 同步代码运行完之后,先运行微队列,再运行宏队列;
- 最后结果为 a c 1 b。
-
pro.then可以只添加thenable函数,pro.catch可以单独添加catchable函数
pro.then(data => { console.log(data) }) pro.catch(err => { console.log(err) }) -
在未决阶段的处理函数中,如果发生未捕获的错误,会将状态推向rejected,并会被catchable捕获
const pro = new Promise((resolve, reject) => { throw new Error('报错'); }) pro.then(data => { console.log(data) }) pro.catch(err => { console.log(err) }) // 输出错误,然后报错 -
一旦状态推向了已决阶段,无法再对状态做任何更改
const pro = new Promise((resolve, reject) => { resolve(1); reject(2); // 无效 resolve(3); // 无效 reject(4); // 无效 })最终状态一定是resolved,后面的代码是无效的。状态是不可逆的。
-
Promise没有消除回调,只是让回调变得可控
总结概述
Promise构造函数的参数是一个函数,这个函数只管把一个状态如何从未决推向已决。剩下的事情是后续处理,后续处理是通过注册then或者catch来进行处理。它把推向和处理是彻底分开的。