Promise
Promises属于es6规范,是异步编程新的解决方案(旧方案是使用回调函数)
从语法上来说,Promises是一个构造函数,从功能上讲,用于表示一个异步操作的最终完成(或失败)及其结果值
Promises使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个Promises,以便在未来某个时候把值交给使用者
为什么使用Promise
常见异步操作
fs 文件操作
jsrequire("fs").readFile("./index.html",(err,data)=>{})
require("fs").readFile("./index.html",(err,data)=>{})
ajax
jsaxios.get('/api',(data)=>{})
axios.get('/api',(data)=>{})
定时器
jssetTimeout(()=>{},2000)
setTimeout(()=>{},2000)
回调的缺点
- 必须在启动异步任务前指定回调函数
- 会出现回调地狱,回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件,回调地狱的情况下代码不易于阅读,不便于异常处理
Promise的优点
- 指定回调函数的方式更加灵活,启动异步任务,返回promise,给promise绑定异步函数
- 支持链式调用,可以解决回调地狱问题
new Promise
Promise接收一个函数,这个函数具有两个参数,这两个参数都是函数,一般通用规范命名为resolve和reject
Promise异步执行这个函数,执行成功调用resolve函数,执行失败调用reject函数
一个 Promise
必然处于以下几种状态之一:
- 待定(pending):初始状态,既没有被兑现,也没有被拒绝
- 已兑现(fulfilled):意味着操作成功完成
- 已拒绝(rejected):意味着操作失败
new Promise((resolve, reject) => {
console.log("初始化")
resolve()
}).then(function (result) {
console.log('成功:' + result);
}).catch(function (reason) {
console.log('失败:' + reason);
});
new Promise((resolve, reject) => {
console.log("初始化")
resolve()
}).then(function (result) {
console.log('成功:' + result);
}).catch(function (reason) {
console.log('失败:' + reason);
});
多个异步任务
Promise还可以做更多的事情,比如,有若干个异步任务,需要先做任务1,如果成功后再做任务2,任何任务失败则不再继续并执行错误处理函数。
要串行执行这样的异步任务,不用Promise需要写一层一层的嵌套代码。有了Promise,我们只需要简单地写:
job1.then(job2).then(job3).catch(handleError)
job1.then(job2).then(job3).catch(handleError)
Promise.prototype.then()
返回一个 Promise。它最多需要有两个参数:Promise 的成功和失败情况的回调函数
const promise1 = new Promise((resolve, reject) => {
resolve('Success!')
});
promise1.then((value) => {
console.log(value)
// "Success!"
});
const promise1 = new Promise((resolve, reject) => {
resolve('Success!')
});
promise1.then((value) => {
console.log(value)
// "Success!"
});
参数
onFulfilled
可选当 Promise 变成接受状态(fulfilled)时调用的
函数
。该函数有一个参数,即接受的最终结果(the fulfillment value)。如果该参数不是函数,则会在内部被替换为(x) => x
,即原样返回 promise 最终结果的函数onRejected
可选当 Promise 变成拒绝状态(rejected)时调用的
函数
。该函数有一个参数,即拒绝的原因(rejection reason
)。如果该参数不是函数,则会在内部被替换为一个 "Thrower" 函数 (it throws an error it received as argument)
new Promise((resolve, reject) => {
console.log("初始化")
resolve("成功了")
reject("失败了")
})
.then(() => {
throw new Error("有哪里不对了")
console.log("执行「这个」”")
})
.catch(() => {
console.log("执行「那个」")
})
.then(() => {
console.log("执行「这个」,无论前面发生了什么")
})
console.log(a);
new Promise((resolve, reject) => {
console.log("初始化")
resolve("成功了")
reject("失败了")
})
.then(() => {
throw new Error("有哪里不对了")
console.log("执行「这个」”")
})
.catch(() => {
console.log("执行「那个」")
})
.then(() => {
console.log("执行「这个」,无论前面发生了什么")
})
console.log(a);
Promise.catch
catch() 方法返回一个Promise (en-US),并且处理拒绝的情况。等同于调用Promise.prototype.then(undefined, onRejected)
参数
onRejected
当 Promise 被 rejected 时,被调用的一个
Function
。 该函数拥有一个参数:reason
rejection 的原因。如果onRejected
抛出一个错误或返回一个本身失败的 Promise , 通过catch()
返回的 Promise 被 rejected;否则,它将显示为成功(resolved)
返回值
一个Promise
.
Promise.resolve
参数
value
将被 Promise 对象解析的参数,也可以是一个Promise 对象,或者是一个 thenable。
返回值
返回一个带着给定值解析过的 Promise 对象,如果参数本身就是一个 Promise 对象,则直接返回这个 Promise 对象
const promise1 = Promise.resolve(123);
promise1.then((value) => {
console.log(value);
// expected output: 123
});
const promise1 = Promise.resolve(123);
promise1.then((value) => {
console.log(value);
// expected output: 123
});
Promise.reject
返回一个带有拒绝原因的 Promise
对象
function resolved() {
//不会被调用
}
function rejected(error) {
console.error(error);
}
Promise.reject(new Error('fail')).then(resolved, rejected) // [Error: fail]
function resolved() {
//不会被调用
}
function rejected(error) {
console.error(error);
}
Promise.reject(new Error('fail')).then(resolved, rejected) // [Error: fail]
Promise.reject(new Error("fail")).then(
function () {
//不会被调用
},
function (error) {
console.error(error) // [Error: fail]
}
)
Promise.reject(new Error("fail")).then(
function () {
//不会被调用
},
function (error) {
console.error(error) // [Error: fail]
}
)
Promise.prototype.finally
finally
方法返回一个 Promise
。在 promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行指定的回调函数。这为在 Promise
是否成功完成后都需要执行的代码提供了一种方式
这避免了同样的语句需要在 then()
和 catch()
中各写一次的情况
Promise.race
Promise.race(iterable)
方法返回一个promise,第一个改变状态的promise的结果状态就是最终的结果状态
Promise.finally
finally
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的
链式调用
有了执行函数,我们就可以用一个Promise对象来执行它,并在将来某个时刻获得成功或失败的结果
上一个promise调用resolve会触发then方法,上一个promise调用reject会触发catch方法
因为 Promise.prototype.then
和 Promise.prototype.catch
方法返回的是 promise,所以它们可以被链式调用。
Promise.all
用于并行执行异步任务
试想一个页面聊天系统,我们需要请求两个不同的接口分别获得用户的个人信息和好友列表,这两个任务是可以并行执行的,实现如下:
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([p1, p2]).then(function (results) {
console.log(results); // 获得一个Array: ['P1', 'P2']
});
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([p1, p2]).then(function (results) {
console.log(results); // 获得一个Array: ['P1', 'P2']
});
假如promise.all中有五个异步函数 ,第二个错误了,第三个还会执行吗
如果其中一个函数执行失败(即返回一个 rejected 状态的 Promise),那么整个 Promise.all
将会直接进入 rejected 状态并返回该失败的 Promise 对象,同时剩余的未完成的 Promise 都会被取消。因此,第三个异步函数不会执行
Promise.all会在其中任何一个reject
时立即结束,也就是一个请求失败了就不能进行其余正确的请求了
请求失败不影响其他正确异步
解决方案:map的每一项都是promise,catch方法返回值会被promise.reslove()包裹,这样传进promise.all的数据都是resolved状态的
Promise.all([Promise.reject({ code: 500, msg: "服务异常" }), Promise.resolve({ code: 200, list: [] }), Promise.resolve({ code: 200, list: [] })].map(p => p.catch(e => e)))
.then(res => {
console.log("res=>", res)
})
.catch(error => {
console.log("error=>", error)
})
console.log(Promise.reject({ code: 500, msg: "服务异常" }).catch(e => e)); // Promise { <pending> }
Promise.all([Promise.reject({ code: 500, msg: "服务异常" }), Promise.resolve({ code: 200, list: [] }), Promise.resolve({ code: 200, list: [] })].map(p => p.catch(e => e)))
.then(res => {
console.log("res=>", res)
})
.catch(error => {
console.log("error=>", error)
})
console.log(Promise.reject({ code: 500, msg: "服务异常" }).catch(e => e)); // Promise { <pending> }
自行实现
function myAll(arr) {
let length = arr.length; // 先获取传入的数组的长度
let currentCount = 0; // 记录当前执行的下标
let results = []; // 存储返回结果
return new Promise((resolve, reject) => {
for (let i = 0; i < arr.length; i++) {
Promise.resolve(arr[i]).then((res) => {
results[i] = res; // 存储在results中,下标加一
currentCount++;
// 如果执行完全部,返回结果
if (currentCount === length) {
resolve(results)
}
}).catch((err) => {
reject(err) // 如果其中一个执行失败,直接reject
})
}
// 如果传入空数组,直接return results
if(length === 0) {
resolve(results)
}
})
}
function myAll(arr) {
let length = arr.length; // 先获取传入的数组的长度
let currentCount = 0; // 记录当前执行的下标
let results = []; // 存储返回结果
return new Promise((resolve, reject) => {
for (let i = 0; i < arr.length; i++) {
Promise.resolve(arr[i]).then((res) => {
results[i] = res; // 存储在results中,下标加一
currentCount++;
// 如果执行完全部,返回结果
if (currentCount === length) {
resolve(results)
}
}).catch((err) => {
reject(err) // 如果其中一个执行失败,直接reject
})
}
// 如果传入空数组,直接return results
if(length === 0) {
resolve(results)
}
})
}
改写
function myAll(arr) {
let length = arr.length; // 先获取传入的数组的长度
let currentCount = 0; // 记录当前执行的下标
let results = []; // 存储返回结果
return new Promise((resolve, reject) => {
for (let i = 0; i < arr.length; i++) {
Promise.resolve(arr[i]).then((res) => {
results[i] = res; // 存储在results中
}).catch((err) => {
results[i] = false; // 改动:如果其中一个执行失败,返回一个false
}).finally(() => {
currentCount++; // 无论成功或失败,下标都加一
if (currentCount === length) {
resolve(results)
}
})
}
// 如果传入空数组,直接return results
if(length === 0) {
resolve(results)
}
})
function myAll(arr) {
let length = arr.length; // 先获取传入的数组的长度
let currentCount = 0; // 记录当前执行的下标
let results = []; // 存储返回结果
return new Promise((resolve, reject) => {
for (let i = 0; i < arr.length; i++) {
Promise.resolve(arr[i]).then((res) => {
results[i] = res; // 存储在results中
}).catch((err) => {
results[i] = false; // 改动:如果其中一个执行失败,返回一个false
}).finally(() => {
currentCount++; // 无论成功或失败,下标都加一
if (currentCount === length) {
resolve(results)
}
})
}
// 如果传入空数组,直接return results
if(length === 0) {
resolve(results)
}
})
Promise.allSettled
该Promise.allSettled()
方法返回一个在所有给定的 promise 都已经fulfilled
或rejected
后的 promise,并带有一个对象数组,每个对象表示对应的 promise 结果
当您有多个彼此不依赖的异步任务成功完成时,或者您总是想知道每个promise
的结果时,通常使用它
相比之下,Promise.all()
更适合彼此相互依赖或者在其中任何一个reject
时立即结束
中断promise
返回一个状态为pending的promise
let p = new Promise((resolve, regect) => {
resolve(1)
})
p.then(value => {
console.log(2);
return new Promise(() => {
})
}).then(() => {
console.log(3);
}).then(() => {
console.log(4);
}).catch(()=>{
console.log("err");
})
let p = new Promise((resolve, regect) => {
resolve(1)
})
p.then(value => {
console.log(2);
return new Promise(() => {
})
}).then(() => {
console.log(3);
}).then(() => {
console.log(4);
}).catch(()=>{
console.log("err");
})
Promise 是用来管理异步编程的,它本身不是异步的
new Promise的时候会立即把executor函数执行,只不过我们一般会在executor函数中处理一个异步操作。例如下面一段代码:
let firstPromise = new Promise(()=>{
setTimeout(()=>{
console.log(1)
},1000)
console.log(2)
})
console.log(3) // 2 3 1
let firstPromise = new Promise(()=>{
setTimeout(()=>{
console.log(1)
},1000)
console.log(2)
})
console.log(3) // 2 3 1
Promise 采用了回调函数延迟绑定技术
在执行 resolve 函数的时候,回调函数还没有绑定,那么只能推迟回调函数的执行。这具体是啥意思呢?我们先来看下面的例子:
let p1 = new Promise((resolve,reject)=>{
console.log(1);
resolve('浪里行舟')
console.log(2)
})
// then:设置成功或者失败后处理的方法
p1.then(result=>{
//p1延迟绑定回调函数
console.log('成功 '+result)
},reason=>{
console.log('失败 '+reason)
})
console.log(3)
// 1
// 2
// 3
// 成功 浪里行舟
let p1 = new Promise((resolve,reject)=>{
console.log(1);
resolve('浪里行舟')
console.log(2)
})
// then:设置成功或者失败后处理的方法
p1.then(result=>{
//p1延迟绑定回调函数
console.log('成功 '+result)
},reason=>{
console.log('失败 '+reason)
})
console.log(3)
// 1
// 2
// 3
// 成功 浪里行舟
new Promise的时候先执行executor函数,打印出 1、2,Promise在执行resolve时,触发微任务,还是继续往下执行同步任务, 执行p1.then时,存储起来两个函数(此时这两个函数还没有执行),然后打印出3,此时同步任务执行完成,最后执行刚刚那个微任务,从而执行.then中成功的方法
promise与async/await
在JavaScript中,Promise是一种用于处理异步操作的机制。它是一个表示操作的未来结果的对象,这个对象可能会被解决(resolved)或者被拒绝(rejected)。
当我们创建一个Promise时,它会立即处于pending状态。我们可以通过调用Promise实例的resolve和reject方法,将其从pending状态转换为resolved或rejected状态。
resolve方法会将Promise从pending状态转换为resolved状态,并将解决结果作为参数传递给then方法的回调函数。这表示异步操作已经成功完成并返回了一个值。
例如,下面的代码创建了一个Promise,然后在1秒后使用resolve方法将其解决:
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Operation completed successfully!");
}, 1000);
});
myPromise.then((result) => {
console.log(result); // "Operation completed successfully!"
});
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Operation completed successfully!");
}, 1000);
});
myPromise.then((result) => {
console.log(result); // "Operation completed successfully!"
});
reject方法会将Promise从pending状态转换为rejected状态,并将错误信息作为参数传递给catch方法的回调函数。这表示异步操作发生了错误并返回了一个错误对象。
例如,下面的代码创建了一个Promise,然后立即使用reject方法将其拒绝:
const myPromise = new Promise((resolve, reject) => {
reject(new Error("Operation failed!"));
});
myPromise.catch((error) => {
console.error(error); // Error: Operation failed!
});
const myPromise = new Promise((resolve, reject) => {
reject(new Error("Operation failed!"));
});
myPromise.catch((error) => {
console.error(error); // Error: Operation failed!
});
总之,Promise对象的resolve方法表示异步操作已成功完成并返回了一个值,而reject方法表示异步操作发生了错误并返回了一个错误对象。
使用async/await
需要使用try/catch接受异步操作生的错误和一个错误对象
const myPromise = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Operation completed successfully!");
// reject("Operation completed err!");
}, 1000);
});
};
async function asyncFunction() {
try {
const res = await myPromise();
console.log(res); // Operation completed successfully!
} catch (err) {
console.log(err);
}
}
asyncFunction();
const myPromise = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Operation completed successfully!");
// reject("Operation completed err!");
}, 1000);
});
};
async function asyncFunction() {
try {
const res = await myPromise();
console.log(res); // Operation completed successfully!
} catch (err) {
console.log(err);
}
}
asyncFunction();
const myPromise = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
// resolve("Operation completed successfully!");
reject("Operation completed err!");
}, 1000);
});
};
async function asyncFunction() {
try {
const res = await myPromise();
console.log(res);
} catch (err) {
console.log(err); // Operation completed successfully!
}
}
asyncFunction();
const myPromise = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
// resolve("Operation completed successfully!");
reject("Operation completed err!");
}, 1000);
});
};
async function asyncFunction() {
try {
const res = await myPromise();
console.log(res);
} catch (err) {
console.log(err); // Operation completed successfully!
}
}
asyncFunction();
async函数返回值
async
函数的返回值是一个 Promise 对象。当函数内部有明确的 return
语句时,该返回值将成为 Promise 的 resolved 值;如果没有明确的 return
语句,该 Promise 的 resolved 值将会是 undefined。
async function asyncFunction() {
console.log(await asyncFunction2()); // 1
}
async function asyncFunction2() {
return 1;
}
async function asyncFunction() {
console.log(await asyncFunction2()); // 1
}
async function asyncFunction2() {
return 1;
}
async function asyncFunction() {
console.log(await asyncFunction2()); // unde
}
async function asyncFunction2() {
}
async function asyncFunction() {
console.log(await asyncFunction2()); // unde
}
async function asyncFunction2() {
}
async函数中返回异步函数的值
async function asyncFunction1() {
let a = 1;
setTimeout(() => {
a = 2;
}, 200);
return a;
}
async function asyncFunction1() {
let a = 1;
setTimeout(() => {
a = 2;
}, 200);
return a;
}
在这个例子中,虽然我们在 setTimeout 中将 a 的值更改为 20,但是由于 setTimeout 是一个异步函数,它会在 200 毫秒后才会被调用。在此之前,asyncFunction1 已经在 200 毫秒之内返回了一个 Promise,Promise 的值是 a 的初始值 1,而不是更改后的值 2。
如果我们想要 asyncFunction1 的结果为更改后的值,可以将 setTimeout 包装成一个 Promise 并在其中返回一个新的值,然后在 asyncFunction1 中使用 await 等待 Promise 的完成,例如:
async function asyncFunction1() {
let a = 1;
await new Promise(resolve => setTimeout(() => {
a = 2;
resolve();
}, 200));
return a;
}
async function asyncFunction1() {
let a = 1;
await new Promise(resolve => setTimeout(() => {
a = 2;
resolve();
}, 200));
return a;
}
为什么resolve后面的代码还会执行
async function asyncFunction1() {
let a = 1;
await new Promise((resolve)=>{
setTimeout(() => {
resolve()
a = 100;
console.log(a); // 100
}, 200);
})
return a;
}
async function asyncFunction2() {
const res = await asyncFunction1();
console.log(res); // 100
}
asyncFunction2()
async function asyncFunction1() {
let a = 1;
await new Promise((resolve)=>{
setTimeout(() => {
resolve()
a = 100;
console.log(a); // 100
}, 200);
})
return a;
}
async function asyncFunction2() {
const res = await asyncFunction1();
console.log(res); // 100
}
asyncFunction2()
Promise实例本身是同步执行的,会直接返回一个pending状态的Promise对象。之后,异步操作会被推到宏任务队列中等待执行。
这是因为在 asyncFunction1()
中,setTimeout()
是一个异步操作,它会在200ms后才会执行其中的回调函数。当 await
等待 Promise
对象被 resolve
时,事件循环将暂停执行 asyncFunction1()
直到 Promise
对象的状态变为 resolved
。在这个过程中,setTimeout()
中的回调函数并没有执行,await
关键字暂停了 asyncFunction1()
的执行直到 Promise
对象被解决。
当 Promise
对象被 resolve
后,它的状态变为 resolved
,然后事件循环将开始执行下一个任务,即 setTimeout()
中的回调函数。在回调函数中,a
的值被更改为 100
,并且打印出了这个值。
最后,Promise
对象在 asyncFunction1()
中被解决,并将 a
的值作为 Promise
对象的值返回。