# promise背景与作用
- 在过去,回调函数层层嵌套,造成回调地狱,使代码难以阅读
- promise 就是将异步操作以同步流程优雅表达出来,避免了层层嵌套
# Promise 构造函数
接收一个同步回调方法,作为参数
# promise 实例的三个状态
- pending:初始化状态......new Promise时触发
- fulfilled:成功状态......resolve()时触发
- rejected:失败状态......reject()时触发
!必须注意:promise实例在实例化时就已经执行了当中的同步代码:如事件绑定、定时器开启、发送请求
# 实例方法
# Promise.prototype.then(?cb1,?cb2)
then 方法可接受两个分别对应 fulfilled 和 rejected 状态的回调函的。若 then 接收函数以外的值作为参数,则忽略这个 then。
注意:promise的失败具有传递性。then的失败回调会捕获到链上的报错。
p1
.then(step1)
.then(step2)
.then(step3)
.then(
console.log,
console.error
);
p1后面有四个then,意味依次有四个回调函数。只要前一步的状态变为fulfilled,就会依次执行紧跟在后面的回调函数。 最后一个then方法,回调函数是console.log和console.error,console.log只显示step3的返回值,而console.error可以显示p1、step1、step2、step3之中任意一个发生的错误。
# then 中两个回调函数的参数
fulfilled 回调中,参数可以是resolve的传参,也可以是上一个then的返回值。 rejected 回调中,参数可以是reject的传参,也可以上之前then链上发生的错误。
观察这个案例:
let func = function() {
return new Promise((resolve, reject) => {
resolve('返回值');
});
};
let cb = function() {
return '新的值';
}
func().then(function () {
return cb();
}).then(resp => {
console.warn(resp);//"新的值"
console.warn('1 =========<');
});
func().then(function () {
cb();
}).then(resp => {
console.warn(resp);//因为上一个回调没有return值,所以是 "undefined"
console.warn('2 =========<');
});
func().then(cb()).then(resp => {
console.warn(resp);// 因为上一个then中的参数是字符串不是函数,忽略上一个then,关注前一个then,"返回值"
console.warn('3 =========<');
});
func().then(cb).then(resp => {
console.warn(resp);// "新的值"
console.warn('4 =========<');
});
# Promise.prototype.catch()
捕获 promise 实例发生的 异常 或 reject
# Promise.prototype.finally()
无论最后是 fulfilled 还是rejected 都会执行 finally 的回调
# 静态方法
# Promise.all()
Promise.all([p1,p2]).then(([r1,r2])=>{});
当p1,p2的状态都为 fulfilled 才执行成功回调。当有一个状态为 rejected 则执行失败回调。
# Promise.allSettled()
Promise.allSettled([p1,p2]).then((result)=>{});
p1,p2都具有状态时,(无论是fulfilled 还是 rejected),都执行成功回调。
# Promise.race()
Promise.race([p1,p2]).then((res)=>{});
p1,p2 以第一个到达 settled 状态的 promise 为基准,具体状态是 fulfilled 则调用成功回调,是 rejected 就调用失败回调。后面的 promise 无论其状态如何都不会触发回调。
# Promise.any()
Promise.any(promises).then( (first) => { // Any of the promises was fulfilled. }, (error) => { // All of the promises were rejected. } );
p1,p2中,有一个 promise 到达 fulfilled 状态就会触发成功回调,仅当所有的 promise 都到达 rejected 时才会触发错误回调。
# Promise.resolve() 重点
Promise.resolve('foo') 等价于 new Promise(resolve=> resolve('foo'))
接收不同参数时,返回有所不同
- 参数是一个Promise实例,返回该实例。
- 不传参或传个普通值,返回一个状态为 fulfilled 的promise实例。
- 参数是一个 thenable 对象,将对象转为 promise实例并返回。 (罕见)
注意:promise.resolve().then()属于微任务。
//2.
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function (value) {
console.log(value); // 42
});
//3.
const p = Promise.resolve('Hello');
p.then(function (s) {
console.log(s)
});
// Hello
# Promise.reject()
返回一个状态为 rejected 的 Promise 实例。
# 任务队列类型
- 事件队列中的任务划分有 Macrotasks宏任务 和 Microtasks微任务 两种
- 在每一次事件循环中宏任务只会提取一个执行,而微任务会一直提取,直到微任务队列为空为止
使用宏任务的API有:
- setTimeout
- setInterval
- setImmediate
- I/0
- UI rendering
使用微任务的API有:
- process.nextTick
- Promise
- MutationObserver
promise的微任务队列 promise的 then链 就是一个个微任务。 当 promise 实例到达 fulfilled 状态时,then中的任务会添加到微任务队列中,等待执行。 intaglio
观察并分析输出:
async function async1() {
console.log('async1 start')//2
await async2()
console.log('async1 end')// 6
}
async function async2() {
console.log('async2')// 3
}
console.log('script start'); // 1
setTimeout(() => {
console.log('setTimeOut')// 8
}, 0)
async1();
new Promise((resolve)=>{
console.log("promise1");// 4
resolve();
}).then(function(){
console.log("promise2");//7
})
console.log("script end"); // 5
# promise基础使用
# 例一:封装AJAX
function AJAX(url) {
let promise = new Promise((resolve, reject) => {
let xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
xhr.open("GET", url);
xhr.send();
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (
(xhr.status >= 200 && xhr.status < 300) ||
xhr.status === 304
) {
// 传入xhr.responseText 作为成功回调的data
resolve(xhr.responseText);
} else {
// 传入“失败了” 作为失败回调的error
reject(new Error("fail"));
}
}
};
});
// 返回一个promise实例
return promise;
}
window.onload = () => {
AJAX("http://jsonplaceholder.typicode.com/posts")
.then((data) => {
console.log("第一次执行成功");
console.log(JSON.parse(data));
// 再返回一个promise实例 可以列式调用
return AJAX("http://jsonplaceholdsts");
})
.then((data) => {
console.log("第二次执行成功");
console.log(JSON.parse(data));
})
.catch((err) => {
console.log(err);
});
};
# 例二:链式调用
只要在then的回调方法中return promise实例,就能链式使用then
const promise = new Promise((resolve) => {
resolve("hello");
});
promise
.then((res) => {
//then中返回一个新的promise
return new Promise((resolve) => {
resolve(res + " world");
});
})
.then((res) => {
// 输出 hello world
console.log(res);
});
# promise进阶
# 例一:执行异步任务队列
// 初始化队列,队列元素是返回promise实例的函数
let tasks = [];
function addAsyncTask(j) {
const task = function () {
return new Promise((resolve) => {
setTimeout(() => {
console.log("exec:", j);
resolve();
}, 1000);
});
};
tasks.push(task);
}
for (var i = 0; i < 5; i++) {
addAsyncTask(i);
}
// 执行队列,任务逐个执行
function startQueue(tasks) {
let index = 0;
return new Promise((resolv, reject) => {
(function execTask() {
if (index === tasks.length) {
resolve();
return;
}
tasks[index]()
.then(() => {
execTask();
})
.catch((err) => {
reject(err);
});
index++;
})();
});
}
startQueue()
.then(() => {
// 全部执行完毕
console.log("执行完毕");
})
.catch((err) => {
// 中间某个任务执行失败,抛出异常
console.warn(err);
});
# 例二:使用 reduce 更简洁地进行队列的执行
function t1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("a");
}, 1000);
});
}
function t2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("b");
}, 1000);
});
}
function t3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("c");
}, 1000);
});
}
const tasks = [t1, t2, t3];
tasks.reduce((chain, task) => {
return chain.then(() => task()).then((res) => {
console.log(res);
});
}, Promise.resolve());
# 例三:实现经典0->1->2->3->4->5
// 定义队列
const tasks = (function () {
return Array.from({ length: 6 }).map((item, index) => {
return function () {
return new Promise((resolve) => {
setTimeout(() => {
resolve(index)
}, 1000)
})
}
})
})()
// 执行队列
tasks.reduce((chain, task) => {
return chain
.then(() => task())
.then((res) => {
console.log(res)
})
}, Promise.resolve())
# 例四:根据例三改一下
输出: 0->1->2->3->stop at 4
为什么会 stop at 4 ?
- 因为到i===4时,reject 导致返回的promise实例变为了 rejected状态,
- 所以他不继续执行当前轮的第二个 then 回调(即,不会输出 4),
- 也无法在下一轮中执行第一个then回调(即,无法返回最后一个任务的promise实例)
let tasks = [];
for (let i = 0; i <= 5; i++) {
tasks.push(function () {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (i === 4) {
reject(i)
}
resolve(i);
}, 1000);
});
});
}
const p = tasks.reduce((chain, task) => {
return chain
.then(() => task())
.then((res) => {
console.log(res);
});
}, Promise.resolve());
p.then(()=>{
console.log("finished");
}).catch(err=>{
console.log("stop at "+err);
})