1.Promise用法
Promise
的then
方法会返回一个新的Promise
let p = function(){
return new Promise((resolve, reject) => {
try {
setTimeout(()=>{
return resolve()
},500)
} catch (error) {
return reject(error)
}
})
}
async function do(){
let res = await p()
console.log(res)
}
p().then((res)=>{
console.log(res)
},(err)=>{
console.log(err)
})
p().then((res)=>{
console.log(res)
}).catch((err)=>{
console.log(err)
})
p().then((res)=>{
console.log(res)
return p()
}).then((res)=>{
console.log(res)
}).catch((err)=>{
console.log(err)
})
2.Promise.all用法(ES11)
Promise.all
可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
另外Promise.allSettled
看起来像是对Promise.all
的一种补充,缓解了使用Promise.all
碰到reject
的痛点问题。
一句话概括Promise.allSettled
和Promise.all
的最大不同:Promise.allSettled
会执行所有的Promise。而Promise.all
只要遇到第一个reject
的结果,整体就会reject
。
Promise.allSettled
执行后返回的是对象数组,每个对象中包含了各Promise
的执行状态(fulfilled
和rejected
)和结果值。
Promise.allSettled()
和Promise.all()
用来做批量异步处理。如果说每一个异步都需要得到结果,就用allSettled()。如果说每一个异步都需要成功,才能往下进行,就用all ()。
let p1 = new Promise((resolve, reject) => {
resolve('成功了')
})
let p2 = new Promise((resolve, reject) => {
resolve('success')
})
let p3 = Promse.reject('失败')
Promise.all([p1, p2]).then((result) => {
console.log(result) //['成功了', 'success']
}).catch((error) => {
console.log(error)
})
Promise.all([p1,p3,p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 失败了,打出 '失败'
})
//---------------------
async function fn6(){
let [a,b,c] = await Promise.all([
await '成功了a',
new Error('失败了b'),
await '成功了c',
])
return [a,b,c]
}
fn6().then(res=>{
console.log('fn6-res',res) //fn6-res [ '成功了a', Error: 失败了b, '成功了c' ]
})
//------------------------------
async function foo(){
return Promise.allSettled([
await new Promise((resolve) => setTimeout(() => resolve("1"), 2000)),
await new Promise((resolve, reject) => setTimeout(() => resolve("2")))
]);
}
foo().then((data) => {
console.log(data); //[{status: 'fulfilled', value: '1'},{status: 'fulfilled', value: '2'}]
})
3.promise原理
promise的核心原理其实就是发布订阅模式,通过两个队列来缓存成功的回调(onResolve)和失败的回调(onReject)。实则也是通过回调函数方式实现异步。
4.promise特点
Promise
状态不受外部影响。Promise
有三个状态:pending
进行中、fulfilled
已成功、rejected
已失败。只有异步操作的结果才可以决定当前是哪个状态,其他任何操作都无法改变状态。(这也就是promise许诺的由来)- 一旦状态改变,就不会再变。
Promise
对象状态改变只有两种可能:从pending
改到fulfilled
或者从pending
改到rejected
,只要这两种情况发生,状态就凝固了不会再变。这时候就称为定型resolved
。
5.原生实现Promise(手写Promise)
使用发布订阅模式+状态切换原理实现。由于原型方法诸如then、all
function MyPromise(execution) {
// 静态属性(常量)
MyPromise.PENDING = 'pending'
MyPromise.FULFILLED = 'fulfilled'
MyPromise.REJECTED = 'rejected'
this.status = MyPromise.PENDING // 状态
this.reason = null // 失败原因
this.value = null // 成功返回值
// 收集依赖缓存(函数)
this.resolveDeps = []
this.rejectDeps = []
// 以上的遍历也可设为私有变量,再添加get/set方法,这里为简化没做
// this缓存
var self = this
// 成功回调
function resolve(res) {
// 判断状态
if (self.status === MyPromise.PENDING) {
self.value = res
self.status = MyPromise.FULFILLED
// 这种方式可以遍历数组且执行后可以清空数组,简化清空数组的步骤
while (self.resolveDeps.length) {
// 发布给所有订阅
self.resolveDeps.shift()(res)
}
}
}
// 失败回调
function reject(err) {
// 判断状态
if (self.status === MyPromise.PENDING) {
self.reason = err
self.status = MyPromise.REJECTED
// 发布给订阅
while (self.rejectDeps.length) {
self.rejectDeps.shift()(err)
}
}
}
// 判断参数传入是否是函数
if (typeof execution === 'function') {
// 捕获错误
try {
// 执行传入的执行器
execution(resolve, reject)
} catch (err) {
reject(err)
}
} else {
throw 'MyPromise 的参数必须为函数。'
}
}
MyPromise.prototype.then = function(onResolved, onRejected) {
if ((onRejected && typeof onRejected !== 'function') || typeof onResolved !== 'function') {
throw 'then 方法的参数必须为函数。'
}
// then方法链式 返回新promise
return new MyPromise((resolve, reject) => {
const resolvedCb = (val) => {
let nextVal = onResolved(val);
if (nextVal instanceof MyPromise) { // promise对象
nextVal.then(resolve, reject)
} else { // 普通值
resolve(nextVal);
}
}
const rejectedCb = (reason) => {
let nextVal = onRejected(reason);
if (nextVal instanceof MyPromise) { // promise对象
nextVal.then(resolve, reject)
} else { // 普通值
resolve(nextVal)
}
}
// 执行器是异步操作时,status还是pending,
// 所以此时并不知道是成功还是失败回调,那么这里就需要把这两个回调
// 存储起来
this.resolveDeps.push(resolvedCb)
this.rejectDeps.push(rejectedCb)
})
}
MyPromise.prototype.catch = function(onRejected) {
if (typeof onRejected === 'function') {
// 收集依赖
this.addRejectDeps(onRejected)
} else {
throw 'catch 方法的参数必须为函数。'
}
}
MyPromise.prototype.finally = function() {}
MyPromise.prototype.all = function() {}
MyPromise.prototype.allSettled = function() {}
// 测试
var demo = function() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(123435)
}, 1000)
})
}
demo().then((res) => {
console.log('=========res', res)
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('erersadad')
}, 1000)
})
}).then((res) => {
console.log('=========res2', res)
})
6.promise和async/await、generator区别
Async/await 是Javascript编写异步程序的新方法。以往的异步方法无外乎回调函数和Promise。但是Async/await建立于Promise之上。
async/await:
- async/await 是写异步代码的新方式,以前的方法有回调函数和Promise。
- async/await 是基于Promise实现的,它不能用于普通的回调函数。
- 在主体函数之前使用了async关键字,在函数体内,使用了await关键字。
- wait关键字只能出现在用async声明的函数体内。
- 当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
- async/await 与Promise一样,是非阻塞的。
- async/await 使得异步代码看起来像同步代码。
promise和async的区别:
- async/await 更加语义化,更简洁
- async/await可以使用try{}catch(){}捕获错误
- 深层嵌套可以使用promise.then方法进行链式请求,也可以使用promise.all方法。但最简洁的方式是使用async
- async 的优越性就是把每次异步返回的结果从 then 中拿到最外层的方法中,不需要链式调用
async 和 Generator区别:
- async是generator函数的语法糖。
- generator 函数是将函数分步骤阻塞 ,只有主动调用 next() 才能进行下一步
var gen1 = function* () {
var f1 = yield 1;
var f2 = yield 2;
console.log(f1);
console.log(f2);
};
var aaa = gen1() //undefined
aaa.next() //{value: 1, done: false} //执行f1
aaa.next() //{value: 2, done: false} //执行f2
aaa.next()
//undefined
//undefined
//{value: undefined, done: true} 最终执行完毕
async对Generator函数做了以下4点改变:
- 内置执行器: async函数自带执行器,简单的说async函数就相当于自执行的Generator函数
- 更好的语义: async表示函数里有异步操作,await表示紧跟再后面的表达式需要等待结果
- 更广的适用性: yield命令后,只能是Thunk函数或Promise对象,而async函数的await命令后面,可以是Promise对象和原始类型的值
- 返回值是Promise对象,可以使用then方法指定下一步操作
7.Promise如何转async/await(异步转同步)
new Promiose()
转async
async function getdata (params) {
return 'hllo Nodejs'
}
//等价于
function getdata () {
return Promise.resolve('hllo Nodejs')
}
// async 返回 reject
async function getdata2 (params) {
return Promise.reject(1234)
}
// 或者
async function aaa (){
throw 1234
}
// 使用then执行async
getdata2().catch(a=>console.log(a)) //1234
Promiose.then()
转await
async function fooAsync () {
const data = await 1
console.log(data)
}
//等价于
function fooAsync () {
return Promise.resolve(1).then((data) => console.log(data))
}
await
后面可以是Promise
或任意表达式,async
函数返回的也是Promise
async function foo1() {
const result1 = await new Promise((resolve) => resolve('1'))
const result2 = await new Promise((resolve) => resolve('2'))
return result1 + result2
}
async function foo2() {
const result = await foo1()
}
async
返回一个数组
内部是使用Promise.all()
方法来执行,返回可迭代的数组,全部成功才返回成功,一个失败所有的都失败。
async function foo() {
const result1 = await new Promise((resolve) => {
setTimeout(() => resolve({ name: "selfsummer" }), 2000);
});
const result2 = await new Promise((resolve, reject) => setTimeout(() => resolve({ name: "自夏" })));
return [result1, result2];
}
foo().then((data) => {
Array.isArray(data) // true
console.log(data) //[ { name: 'selfsummer' }, { name: '自夏' } ]
}).catch((err) => {
console.log(err);
});
8.promise和setTimeout区别
Promise
本身是同步的立即执行函数, 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行
setTimeout
不会阻碍后续代码执行,会放到任务队列中。可以理解为一个异步函数。但论其原理实则不是严格意义上的异步,而只是推迟处理。
Promise
中的异步操作(promise.then)是微任务。而setTimeout
属于宏任务。微任务要优先于宏任务执行。
https://code.zuifengyun.com/2021/03/1810.html
9.promise是异步还是同步
promise本身是同步的,其then方法是异步的(promise.then属于异步微任务,then中的方法,必须等到所有的同步任务执行完才执行)。promise是无法取消的,一旦新建就会立即执行。如果不设置回调函数,promise内部的错误是无法反应到外部的。当处于“pending”状态时,无法得知目前进展到哪个阶段(刚刚开始还是即将完成)。
异步的三种实现方式:
- 回调函数(回调函数不一定是异步 , 但异步一定有回调函数)
- 事件
- promise 对象
10.Promise在resolve和reject执行之后,后续代码还会执行吗
会的。因为resolve和reject本身是回调函数,无法中断后续代码。需要使用return去中断。所以一般使用默认加return较妥。
function p() {
return new Promise((resolve, reject) => {
console.log(222)
setTimeout(() => {
console.log('async in p')
reject('err in p')
console.log('content after p reject')
}, 200)
})
}
function p1() {
return new Promise((resolve, reject) => {
console.log(333)
setTimeout(() => {
console.log('async in p1')
resolve('resolev in p1')
console.log('content after p1 resolve')
}, 250)
})
}
function p2() {
return new Promise((resoleve, reject) => {
console.log(444)
setTimeout(() => {
console.log('async in p2')
return reject(new Error('p2error'))
console.log('content after p2 return')
}, 300)
})
}
10.Promise的resolve和reject能否用return和throw代替
return
无法取代resolve
,因为return
无法改变 <pending>
状态为<fulfilled>
,在底层,需要执行resolve
回调函数,才能改变状态。
throw
可以取代reject
,因为在底层,会有try/catch
的错误捕获,如若出错,会将状态置为<rejected>
当然,在async函数中可以使用return和throw实现。
本文固定连接:https://code.zuifengyun.com/2019/05/3350.html,转载须征得作者授权。