promise是同步还是异步的?
promise 本身是同步的, 执行的结果先打印出1再打印出2,如果 promise 是异步的应该先打印出 2,所以 promise 本身是同步
let oP = new Promise( (res, rej) => {
console.log(1);
});
console.log(2);
promise 的回调 then 是异步的, 执行的结果1,2,3,因为then是异步的,所以先打印了2,最后再执行回调打印出3
let oP = new Promise((res, rej) => {
console.log(1);
res(3)
});
oP.then((res) => {
console.log(res);
});
console.log(2);
一、promise
0. Promise三种 状态
Promise 是 ES6 提供的一种异步解决方案,比回调函数更加清晰明了。
Promise 有三种状态,分别是:1.等待中(pending)2.完成了 (resolved)3.拒绝了(rejected)
这个状态一旦从等待状态变成为其他状态就永远不能更改状态了,也就是说一旦状态变为 resolved 后,就不能再次改变
new Promise((resolve, reject) => {
resolve('success')
// 无效
reject('reject')
})
当我们在构造 Promise 的时候,构造函数内部的代码是立即执行的
new Promise((resolve, reject) => {
console.log('new Promise')
resolve('success')
})
console.log('finifsh')
// new Promise -> finifsh
Promise 实现了链式调用,也就是说每次调用 then 之后返回的都是一个 Promise,
并且是一个全新的 Promise,原因也是因为状态不可变。如果你在 then 中 使用了 return,
那么 return 的值会被 Promise.resolve() 包装
Promise.resolve(1)
.then(res => {
console.log(res) // => 1
return 2 // 包装成 Promise.resolve(2)
})
.then(res => {
console.log(res) // => 2
})
当然了,Promise 也很好地解决了回调地狱的问题,例如:
ajax(url, () => {
// 处理逻辑
ajax(url1, () => {
// 处理逻辑
ajax(url2, () => {
// 处理逻辑
})
})
})
可以改写成
ajax(url)
.then(res => {
console.log(res)
return ajax(url1)
}).then(res => {
console.log(res)
return ajax(url2)
}).then(res => console.log(res))
1. 小用 Promise
<body>
<button class="btn">抽奖</button>
<script>
function random(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
let btn = document.querySelector('.btn');
btn.addEventListener('click', () => {
let num = random(1, 100);
const p = new Promise((resolve, reject) => {
if (num < 30) {
resolve(num)
} else {
reject(num);
}
})
p.then((value) => {
console.log(`中奖了,您的中奖号码是 ${num}`)
}, (value) => {
console.log(`再加油, 您的号码是 ${num}`)
})
})
</script>
</body>
2. Promise 回调返回的是 Promise 对象
-
Promise 中 then 返回的 result 都是 Promise 对象
-
如果 return 是 Promise 的 reject,是返回失败的状态的 Promise 对象
-
其他的包含 return 的是基本数据类型或者引用类型,返回的都是成功状态的 Promise 对象。
<script>
let p = new Promise((resolve, reject) =>{
resolve('成功')
})
let result = p.then(res => {
// return res
// return undefined
return new Promise((resolve, reject) => {
// resolve('success')
reject('fail')
})
})
// console.log(result, 'res')
// console.log(result, 'undefined')
// console.log(result, 'success')
console.log(result, 'fail')
</script>
3. Promse 多次进行回调是否执行
一个 Promise 指定多个成功/失败回调函数,当 Promise 改变对应状态时,多个成功/失败回调函数会被调用,不然不会被调用
let p = new Promise((resolve, reject) => {
// 这里不改变状态, 就不会执行下面的then 回调函数。
resolve('姓名:')
})
p.then(res => {
console.log(res)
})
p.then(res => {
console.log(`${res}xxl`)
})
4. 改变 Promise 状态的三种方法
Promise 有三种状态, pending, resolve, reject
改变 Promise 对象的状态有 三 种:第1种,【调用 resolve】; 第2种:【调用 reject】; 第3种,【抛出错误】
let p = new Promise((resolve, reject) =>{
// resolve('成功')
// reject('失败')
throw('错了')
})
p.catch(reson => {
console.log(reson, 'reson')
})
console.log(p, 'p')
5. 中断 Promise 链
如何中断 Promise 链?
当使用 promise 的 then 链式调用时,要在中间中断,不再调用后面的回调函数
办法: 在回调函数中返回一个 pending 状态的 promise 对象 return new Promise(() => {})。
let p = new Promise((resolve, reject) => {
resolve('success')
})
p.then((res => {
console.log(111)
return new Promise((resolve, reject) => {
resolve('成功')
})
})).then(res => {
console.log(222)
// 回调函数中返回一个 pending 状态的 promise 对象
return new Promise(() => {})
}).then(res => {
console.log(333)
})
6. Promise 异常穿透 catch
Promise 异常穿透: 当使用 promise 的 then 链式调用时,可以在最后指定失败的回调。前面任何操作出了异常,都会传到最后失败的回调中进行处理。
let p = new Promise((resolve, reject) => {
resolve('成功')
// reject('错了')
})
p.then(res => {
console.log(res, 1)
return new Promise((resolve, reject) => {
resolve('success')
})
}).then(res => {
console.log(res, 2)
}).then(res => {
throw '错啦'
console.log(res, 3)
}).catch(reson => {
console.log(reson, 'reson')
})
第一种情况:Promise 内部抛出异常, 通过 then 的第二个函数 err 来捕捉异常。
// Promise 的异常捕获问题
const promise = new Promise((resolve,reject) => {
throw new Error('test')
})
//
promise.then(res => {
console.log(res);
},err => {
// 通过then的第二个函数来进行捕捉
console.log(err); // [Error test]
})
第二种情况:Promise.then 的第一个函数出现了异常, 通过catch 来捕捉 Promise.then 的第一个函数。
const promise = new Promise((resolve,reject) => {
resolve(666)
})
// promise.then 通过.catch 进行捕捉
promise.then(res => {
throw new Error('test2')
},err => {
console.log(err);
}).catch(err => {
console.log(err); //[Error test2]
})
7. then 方法异步执行的回调函数顺序
下面的输出顺序为 111, 333, 222 。
js 从上到下执行, new promise 是微任务,先执行,接下来是同步 console.log(333), then 中是异步执行的回调函数,最后执行。
let p = new Promise((resolve, reject) =>{
resolve('ok');
console.log(111)
})
p.then(v => {
console.log(2222)
})
console.log(333)
8. Promise 链式调用 then 后面接着跟 then
- Promise 对象 then 只要有 return (返回值),都是返回一个 Promise 对象,后面紧跟着链式调用的值都是有值的;
- 如果 then 中没有 return(返回值),后面紧跟着链式调用的值为 undefined
9. resolve 方法
- Promise.resolve 如果传参是一个非 Promise 对象,则返回结果为一个成功的 Promise 对象
let p1 = Promise.resolve('xxl');
console.log(p1)
// 结果:
Promise {<resolved>: "xxl"}
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: "xxl"
- Promise.resolve 如果传参是一个 Promise 对象,则 这个 Promise 对象的参数返回结果决定 P2是成功的 Promise 对象还是一个失败的 Promise对象
let p2 = Promise.resolve(new Promise((resolve, reject) => {
resolve('成功')
}))
console.log(p2) // p2 为成功的 Promise 对象
let p3 = Promise.resolve(new Promise((resolve, reject) => {
reject('失败')
}))
// 防止p3 作为一个失败的 Promsie报错,进行 catch
p3.catch(res => {})
console.log(p3') // p3 为失败的 Promise 对象
10. reject 方法
Promise.reject 无论传入什么参数包括返回成功的 promsie 对象,返回 整体就是一个失败的 Promise 对象。
let p1 = Promise.reject('xxl');
console.log(p1)
let p2 = Promise.reject(new Promise((resolve, reject) =>{
resolve('成功')
}))
p2.catch(res => {
console.log(res, 'res')
})
console.log(p2)
11. all 方法
Promise.all 传参 参数为由 Promise 对象组成的数组
如果 其中一个Promise 对象返回的是失败,则 整体 是直接返回这一个失败的 Promise 对象,其他的后续就不管了
如果 参数里 Promise 对象全部返回的是成功的 Promise 对象,则 整体 是返回一个成功的 Promise 对象。
let p1 = new Promise((resolve, reject) => {
resolve('成功')
})
let p2 = Promise.resolve('success');
let p3 = Promise.resolve('o Year');
let pa = Promise.reject('失败了a');
let pb = Promise.reject('失败了b');
let pSuccess = Promise.all([p1, p2, p3])
let pFail = Promise.all([p1, pa, pb, p3])
pSuccess.then((res) => {
console.log(res) // ["成功", "success", "o Year"]
console.log(res[0]) // 成功
})
pFail.catch((reson) => {
console.log(reson) // 失败了
})
12. race 方法
Promise.race 接收 一个由 Promise 对象组成的参数
Promise.race 参数中第一个返回成功的 Promise 对象作为返回的结果。
let p1 = new Promise((resolve, reject) => {
resolve('成功')
})
let p2 = Promise.resolve('success');
let p3 = Promise.resolve('O Year');
let p4 = Promise.reject('fail 了');
const pSuccess = Promise.race([p2, p1, p3]);
pSuccess.then(res => {
console.log(res) // success
})
const pFail = Promise.race([p4]);
pFail.catch((e) => {
console.log(e) // fail 了
})
13. async 函数
async 函数 特点:
- async 函数的
返回值为 Promise 对象, promise对象的结果由 async 函数执行的返回值决定 - 如果 async 函数 返回的是一个非 promise 值,则返回一个成功的promise 对象
- async 函数 和 Promise 中的 then 方法返回规则一致
async 函数的 返回值为 Promise 对象
async function box() {
return 'xxl'
}
let result = box();
console.log(result)
// 结果:
Promise {<resolved>: "xxl"}
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: "xxl"
如果 async 函数返回的是一个非 promise 值,则返回一个成功的promise 对象 async 函数 和 Promise 中的 then 方法返回规则一致
async function box() {
return 12
}
let result = box();
result.then(res => {
console.log(res) // 12
})
promise 对象的结果由 async 函数执行的返回值决定
async function box () {
return new Promise((resolve, rejejct) => {
throw '错误啦'
})
}
let result = box();
result.catch(err =>{
console.log(err) // 错误啦
})
14. await 函数
await 表达式:
- await 右侧的表达式一般为 Promise 对象,但也可以是其他值
- 如果 表达式是 promise 对象,await 返回的是 promise 成功的值。如果表达是其他值,直接将此值作为 await 的返回值
- await 必须写在 async 函数中,但 async 函数中可以没有 await
- 如果 await 的 promise 失败了,就会抛出异常,需要通过 try...catch 捕获处理
async function box() {
// 右侧跟着非 Promise对象,直接返回值
// let res = await null;
// console.log(res) // null
let p = new Promise((resolve, reject) => {
resolve('1111')
// reject('333')
// throw '444'
})
try {
// await 右侧跟着 promise 对象
let res = await p;
console.log(res, 'res')
} catch (e) {
console.log(e)
}
}
box()
15. async 与 await 结合使用
利用 async 函数 结合 await 来获取各个文件的内容
let fs = require('fs');
let util = require('util');
// util.promisify 可以把对象转化成 promise 对象
let mineReadFile = util.promisify(fs.readFile);
// 利用回调函数进行无限嵌套来获取每个文件的内容
// fs.readFile(('./resouce/1.html'), (err, data1) => {
// fs.readFile(('./resouce/2.html'), (err, data2) => {
// fs.readFile(('./resouce/3.html'), (err, data3) => {
// console.log(data1.toString() + data2.toString() + data3.toString())
// })
// })
// })
// 利用 async 函数 结合 await 来获取各个文件的内容
async function box() {
// 用 try...catch来进行错误捕获
try {
let data1 = await mineReadFile('./resouce/1.html');
let data2 = await mineReadFile('./resouce/2.html');
let data3 = await mineReadFile('./resouce/3.html');
console.log(data1.toString() + data2.toString() + data3.toString())
} catch (e) {
console.log(e.code)
}
}
box()
获取文件内容如下
15. 能用 return 代替 resolve 吗
不能, 通过 return 无法改变 promise 的状态,无法改变状态,也就无法进行 then 链式调用了。
let p = new Promise((resolve, reject) => {
return ('123')
})
p.then(res => {
console.log(res, '---') // 这里不会进行输出
})
16. resolve 后面的 代码还会执行吗?
会执行
let p = new Promise((resolve, reject) => {
resolve('456')
console.log('0000')
})
p.then(res => {
console.log(res)
})
// 结果:
000
456
16.自己手动写 Promise
index.html
<body>
<script>
let p = new Promise((resolve, reject) => {
/**
* Promsie 同步任务:指的是直接在这块调用 resolve() 或 reject() 或 throw 抛出错误
* Promise 异步任务:指的是在这里进行 数据请求,或 setTimeout 等等。
*/
// 异步任务回调的执行,这里用 setTimeout 来模拟接口请求
// setTimeout(() => {
// // reject('fail')
// }, 1000);
})
// p.then(value =>{
// console.log(111)
// }).then(value =>{
// console.log(222)
// }).then(value => {
// console.log(333)
// }).catch(reason =>{
// console.log(reason, 'reason')
// })
// 自定义封装 Promise.resolve方法
// let p2 = Promise.resolve('ok');
// let p2 = Promise.resolve(new Promise((resolve, reject) => {resolve('222s')}));
// let p2 = Promise.resolve(Promise.resolve('333'));
// console.log(p2)
// 自定义封装 Promsie.reject 方法
// let p2 = Promise.reject('fail');
// let p3 = Promise.reject(new Promise((resolve, reject) => {
// resolve('333')
// }));
// let p4 = Promise.reject(Promise.reject('444'))
// console.log(p4)
// 自定义封装 Promise.all 方法
// let p2 = Promise.resolve('ok');
// let p3 = Promise.reject('fail');
// let p4 = Promise.resolve('year');
// let result = Promise.all([p2, p3, p4]);
// console.log(result, 'result')
// 自定义封装 Promise.race 方法
// let p2 = Promise.resolve('Ok');
// let p3 = Promise.reject('fail');
// let p4 = Promise.resolve('success');
// let result = Promise.race([p2, p3, p4]);
// console.log(result)
// 自定义封装 then 方法异步回调函数的执行
// let p2= new Promise((resolve, reject) =>{
// resolve('ok');
// console.log(111)
// })
// p2.then(v => {
// console.log(2222)
// })
// console.log(333)
// 自定义 class 类封装
let p2 = Promise.resolve('333');
console.log(p2)
</script>
</body>
自己手动封装的 classPromise.js
class Promise {
constructor(executor) {
// 设置属性
this.PromiseState = 'pending'; // 设置 Promise 初始状态为 pending
this.PromiseResult = null; // 设置 Promise 返回结果为 null;
this.callBacks = []; // 默认异步的回调对象为空,指定多个回调的实现用数组形式
// console.log(this, 'this') // 这里的 this 指下 Promise 构造函数
const _this = this; // 保存 this 构造函数的作用域
// resolve函数
function resolve(data) {
// console.log(this, '_this') // 这里的 this 指向 window, 需由上面进行保存下
// 确保 promise状态只能改变一次,通过判断当前Promise 的状态
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'fulfilled'; // 设置 Promise 状态为成功状态
_this.PromiseResult = data; // 设置 Promise 输出结果为 data
// 为了让 then 方法中的函数进行异步执行,通过用 setTimeout 让它加入队列
setTimeout(() => {
// 在状态改变后进行成功的回调
_this.callBacks.forEach(item => {
// 执行成功的回调函数
item.onResolve(data)
})
});
}
// reject 函数
function reject(data) {
// 确保 promise状态只能改变一次,通过判断当前Promise 的状态
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'rejected'; // 设置 Promise 状态为失败状态
_this.PromiseResult = data; // 设置 Promise 输出结果为 data
// 为了让 then 方法中的函数进行异步执行,通过用 setTimeout 让它加入队列
setTimeout(() => {
// 在状态改变后进行成功的回调
_this.callBacks.forEach(item => {
// 执行失败的回调函数
item.onReject(data)
})
});
}
// 封装自定义抛出错误
try {
executor(resolve, reject)
} catch (e) {
// 失败去执行 reject 函数
reject(e)
}
}
// 添加 then 方法
then(onResolve, onReject) {
// 为了异常穿透与值进行传递
if (typeof onResolve !== 'function') {
onResolve = reason => reason;
}
// 为了异常穿透与值进行传递
if (typeof onReject !== 'function') {
onReject = reason => {
throw reason
};
}
const _this = this;
return new Promise((resolve, reject) => {
function callBack(type) {
try {
let result = type(_this.PromiseResult);
if (result instanceof Promise) {
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
// 结果的对象状态为【成功】
resolve(result)
}
} catch (e) {
reject(e)
}
}
// 成功执行该回调
if (this.PromiseState === 'fulfilled') {
// 为了让 then 方法中的函数进行异步执行,通过用 setTimeout 让它加入队列
setTimeout(() => {
callBack(onResolve);
});
}
// 失败执行该回调
if (this.PromiseState === 'rejected') {
// 为了让 then 方法中的函数进行异步执行,通过用 setTimeout 让它加入队列
setTimeout(() => {
callBack(onReject);
});
}
// 异步任务在pending的时候保存成功和失败的回调
if (this.PromiseState === 'pending') {
// 保存回调函数,方便Promise异步操作的时候,执行对应的成功或失败的回调函数
this.callBacks.push({
// 保存成功的回调函数
onResolve: function () {
callBack(onResolve);
},
// 保存失败的回调函数
onReject: function () {
callBack(onReject);
},
})
}
})
}
// 添加 catch 方法
catch(onReject) {
return this.then(undefined, onReject)
}
// 添加 resolve 方法, 这里的 resolve 不属于实例对象,属性类,所以我们这里用 static 这个关键字对它进行描述, 表明resolve这里是一个静态成员
static resolve(value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
resolve(value)
}
})
}
// 添加 reject 方法
static reject(value) {
return new Promise((resolve, reject) => {
reject(value)
})
}
/**
* 添加 all 方法,参数为 由一个promise对象组成的数组
* 如果 promise 对象组成的数组都是返回成功的状态,则整体返回是一个成功的状态的 Promise 对象
* 如果 promise 对象组成的数组有一个是返回失败的状态,则整体返回是一个失败的状态的 Promise 对象
*/
static all(promises) {
let arr = []; // 所有promise 返回结果的集合
let count = 0;
// 返回一个 Promise 对象
return new Promise((resolve, reject) => {
for (var i = 0; i < promises.length; i++) {
promises[i].then(v => {
arr[i] = v;
count++;
if (count == promises.length) {
resolve(arr)
}
}, r => {
reject(r)
})
}
})
}
/**
* 添加 race 方法
* 谁先返回就用谁的状态
*/
static race(promises) {
return new Promise((resolve, reject) => {
for (var i = 0; i < promises.length; i++) {
promises[i].then(v => {
// 修改返回对象的状态为【成功】
resolve(v)
}, r => {
// 修改返回对象的状态为【失败】
reject(r)
})
}
})
}
}
二、async 与 promise 的区别
async/await 是基于promise实现的,他不能用于普通的回调函数
async/await 使得异步代码看起来像同步代码
async/await 与 Promise 一样,是非阻塞的。
不同
async关键字。await 关键字只能用在 async 定义的函数内。async 函数会引式返回一个 promise,改 promise 的 resolve 值就是函数 return 的值。
简洁:使用 async 和 await 明显节约了不少代码,不需要.then,不需要写匿名函数处理 promise 的 resolve 的值,不需要定义多余的 data 变量,还避免了嵌套代码。async/await让 try/catch 可以同时
处理同步和异步错误。try/catch 不能处理JSON.parse的错误,因为他在promise中。此时需要.catch,这样的错误处理代码非常冗余。并且,在我们的实际生产代码会更加复杂