promise是什么?
回调函数真正的问题在于他剥夺了我们使用 return 和 throw 这些关键字的能力。而 Promise 很好地解决了这一切。
1. 主要用于异步计算
2. 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
3. 可以在对象之间传递和操作promise,帮助我们处理队列
语法:
new Promise( function(resolve, reject)
{...} /* executor */
);
executor是带有 resolve 和 reject 两个参数的函数 。Promise构造函数执行时立即调用executor 函数, resolve 和 reject 两个函数作为参数传递给executor(executor 函数在Promise构造函数返回所建promise实例对象前被调用)。
创建Promise:
Promise 对象是由关键字 new 及其构造函数来创建的。该构造函数会把一个叫做“处理器函数”(executor function)的函数作为它的参数。这个“处理器函数”接受两个函数——resolve 和 reject ——作为其参数。当异步任务顺利完成且返回结果值时,会调用 resolve 函数;而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject 函数
const myFirstPromise = new Promise((resolve, reject) => {
// ?做一些异步操作,最终会调用下面两者之一:
//
// resolve(someValue); // fulfilled
// ?或
// reject("failure reason"); // rejected
});
想要某个函数拥有promise功能,只需让其返回一个promise即可。
function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(xhr.statusText);
xhr.send();
});
};
Promise 对象的两个特点。
(1)对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。
Promise 也有一些缺点。首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
var promise = new Promise(function(resolve, reject) {
if (/* 异步操作成功,抛出状态 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function(value) {
// success
}, function(value) {
// failure
});
Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 方法和 reject 方法。
如果异步操作成功,则用 resolve 方法将 Promise 对象的状态,从「未完成」变为「成功」(即从 pending 变为 resolved);
如果异步操作失败,则用 reject 方法将 Promise 对象的状态,从「未完成」变为「失败」(即从 pending 变为 rejected)。
resolved 这是一个正常的已决阶段下的状态,后续处理为thenable
rejected 这是一个非正常的已决阶段下的状态,后续处理为catchable
注意: 如果一个promise对象处在fulfilled或rejected状态而不是pending状态,那么它也可以被称为settled状态。你可能也会听到一个术语resolved ,它表示promise对象处于settled状态。关于promise的术语, Domenic Denicola 的 States and fates 有更多详情可供参考。
方法
-
Promise.all(iterable)
这个方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。const proms = []; for (let i = 0; i < 10; i++) { proms.push(new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() < 0.9) { console.log(i, "成功"); resolve(i); } else { console.log(i, "失败") reject(i); } }) })) } const pro = Promise.all(proms); pro.then(data => { console.log("全部成功", data) }, err => { console.log("有失败的", err) }) //只有生成的随机数全部小于0.9的时候,才会推出resolve成功状态,并输出“全部成功”,否则输出“有失败的”
-
Promise.race(iterable)
当iterable参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。function getRandom(min, max) { //promise完成时间 return Math.floor(Math.random() * (max - min) + min); } const proms = []; for (let i = 0; i < 10; i++) { proms.push(new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() < 0.5) { console.log(i, "成功") resolve(i) } else { console.log(i, "失败") reject(i) } }, getRandom(1000, 5000)) })) } const pro = Promise.race(proms) pro.then(data => { console.log("有promise完成", data) }, err => { console.log("有promise失败的", err) }) //只要有一个成功的状态则立即推出这个状态,并调用对应的处理函数
-
allSettled()
等到所有promises都完成(每个promise返回成功或失败)。
返回一个promise,该promise在所有promise完成后完成。并带有一个对象数组,每个对象对应每个promise的结果。const promise1 = Promise.resolve(3); const promise2 = new Promise(function(resolve, reject) { setTimeout(reject, 100, 'foo') }); const promises = [promise1, promise2]; Promise.allSettled(promises). then(function(results) { results.forEach(function(result) { console.log(result.status) }) }); // 输出 fulfilled // 输出 rejected
-
reject()方法返回一个带有拒绝原因的Promise对象。
静态函数Promise.reject返回一个被拒绝的Promise对象。通过使用Error的实例获取错误原因reason对调试和选择性错误捕捉很有帮助。Promise.reject(new Error('fail')).then(function() { // not called }, function(error) { console.error(error); // 输出: Error: fail });
-
resolve() 方法返回一个以给定值解析后的Promise 对象。
如果这个值是一个 promise ,那么将返回这个 promise ;如果这个值是thenable(即带有"then" 方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;否则返回的promise将以此值完成。const promise1 = Promise.resolve(123); promise1.then((value) => { console.log(value);//输出:123 }); // 或者下面写法 const promise1 = Promise.resolve(123).then( function(value) { console.log(value); //输出:123 }, function(value) {//当Promise推出的是reject状态时调用 // 不会被调用 });
个人总结:
有六个注意点:
1.未决阶段下的处理函数是同步的,会立即执行。
2. thenable和catchable函数是异步的,就算会立即执行,也会加入等待队列中的微任务。
const pro = new Promise((resolve, reject) => {
console.log("a");
reject(123)
setTimeout(() => {
console.log("b");
})
})
pro.then(data => {
console.log(data);
})
pro.catch(err => {
console.log(err);
})
console.log(c);
//输出: a
//报错: c is not defined
//输出:123
//输出:Uncaught (in promise) 123
//输出:b
3.pro.then方法可以写thenable和catchable函数,而pro.catch方法只能写catchable函数。
在未决状态的处理函数中,如果发生未捕获的错误,则会将状态推向rejected中, 并且会被catchable所捕获。
const pro = new Promise((resolve, reject) => {
throw new Error("123"); //未捕获的错误,所以进入catch中
})
pro.then(data => {
console.log(data)
}, err => {
console.log(err)
});
// 报错:Error: 123
或者换种写法
pro.catch(err => {
console.log(err); //输出 Error: 123
})
4.一旦状态推向已决阶段,无法做任何改变。
const pro = new Promise((resolve, reject) => {
//resolve与reject只能存在一个, 多个无效, 采取第一次的值
resolve(123)
reject(456)
})
pro.then(data => {
console.log(data);
})
pro.catch(err => {
console.log(err);
})
//输出:123
或者:
const pro = new Promise((resolve, reject) => {
//resolve与reject只能存在一个, 多个无效, 采取第一次的值
throw new Error("123") //未捕获
reject("1564653");
try {
throw new Error("456") //已捕获
} catch (e) {
console.log(e)
}
resolve(123)
reject(456)
})
pro.then(data => {
console.log(data);
})
pro.catch(err => {
console.log(err);//返回 Error: 123
})
上面已经发生了未捕获的错误,已经传值catch中,后面又进行了一次赋值,无法改变,只能保持第一次的值
5.如果前面的promise的后续处理,返回的是一个promise对象,则返回的promise状态和后续处理返回的promise状态保持一致。