文章目录
总结:
-
Promise 是同步代码,Promise.then 是 微任务
-
then 的执行时机是在前面函数执行完成并且 Promise 状态变更以后才会被添加到微任务队列中等待执行
-
then 的 onFulfilled 不是函数,则无效
-
then 的 onFulfilled 是函数,则除了 throw error 其他返回值都会转为 Promise 对象
-
thenable 是微任务
-
thenable 中如果没有执行 resolve(),则 thenable 后面如果还有 then,则不执行
-
await 表达式的 Promise 状态不变更,await 下的代码以及之后的then都不会执行
Promise【ES2015】
promise 为 es6 语法,
Promise 对象是一个构造函数,用来生成Promise实例。
Promise对象,是代表了未来某个将要发生的事件(通常是一个异步操作)
注意:promise里面的内容会直接执行,可在外层使用函数包裹
优点:
- 将异步操作以同步操作的流程表达出来
- 避免了层层嵌套的回调函数(回调地狱),让异步维护更加方便
- 代码可读性更高
最简单写法
function fn() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("异步 11");
resolve();
}, 1000);
});
}
fn();
console.log("22");
实例:请求图片
const loadImg = new Promise((resolve, reject) => {
const img = new Image();
// img.src = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fa3.att.hudong.com%2F61%2F98%2F01300000248068123885985729957.jpg&refer=http%3A%2F%2Fa3.att.hudong.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1621179828&t=4b1d14126d405dbd3a25a7caca3dcde2";
img.src = "https://gimg2.baidu.com/im" // 模拟加载失败
img.onload = (res) => {
// console.log(res);
resolve("图片加载成功");
};
img.onerror = () => {
reject("图片加载失败");
};
});
loadImg.then(
(res) => {
console.log(res);
console.log('成功之后的逻辑')
},
(err) => {
console.log(err);
}
);
promise 三个状态
pending 正在进行中
let p = new Promise(() => {});
console.log(p);
打印结果如下:
fulfilled 已完成
let p = new Promise((resolve, reject) => {
resolve();
});
console.log(p);
打印 resolve() 后的状态
rejected 已拒绝
let p = new Promise((resolve, reject) => {
reject();
});
console.log(p);
在控制台打印 test,打印 reject() 后的状态
promise 对象原型方法
Promise.prototype.then
then(onFulfilled, onRejected)
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("异步 11");
resolve('success');
// reject('error');
}, 1000);
});
p.then(res => {
console.log('完成', res)
}, err => {
console.log('错误', err)
})
onFulfilled
onFulfilled
:成功回调 (res) => {}
当 Promise 变成接受状态(fulfilled)时调用的函数
。该函数有一个参数,即接受的最终结果(the fulfillment value)。
onFulfilled 不是函数:该then无效
then 里接收的是非函数则会将 promise 对象传递到下一个 then 里
then(1):
new Promise((resolve) => {
resolve(0)
}).then(1).then(res => {
console.log(res) // 0
})
如果该参数不是函数,则会在内部被替换为 (x) => x
,即原样返回 promise 最终结果的函数 ——来自MDN
理解为 then(1)
会被替换为 then((x) => x)
,x 也就是 resolve(0)
的值 0。所以 res 得到的是 0
then(thenable):
new Promise((resolve) => {
resolve(0)
}).then({
then(resolve) {
console.log(1)
resolve(2)
}
}).then(res => {
console.log(res) // 0
})
只要onFulfilled不是函数,结果都一样
then(Promise):
new Promise((resolve) => {
resolve(0)
}).then(Promise.resolve(1)).then(res => {
console.log(res) // 0
})
只要onFulfilled不是函数,结果都一样
onFulfilled 是函数:then(() => {})
没有返回任何值
没有返回任何值,那么 then
返回的 Promise 将会成为接受状态,并且该接受状态的回调函数的参数值为 undefined
。
new Promise((resolve) => {
console.log(1)
resolve()
}).then(() => {
console.log(2)
// 无 return,等同于: return Promise.resolve()
}).then(res => {
console.log(res) // undefined
})
打印结果:
1
2
undefined
有返回值
这里值返回不为Promise/thenable 的值
返回了一个值,那么 then
返回的 Promise 将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。
new Promise((resolve) => {
console.log(1)
resolve()
}).then(() => {
console.log(2)
return 100 // 等同于: return Promise.resolve(100)
}).then(res => {
console.log(res) // 100
})
打印结果:
1
2
100
扩展-值是对象:
new Promise((resolve) => {
console.log(1)
resolve()
}).then(() => {
console.log(2)
return { name: '123' } // 等同于: return Promise.resolve({ name: '123' })
}).then(res => {
console.log(res) // {name: '123'}
})
打印结果
1
2
{name: '123'}
返回 thenable
thenable 执行resolve()
返回值返回的是一个对象,且不是promise对象,并且该对象里面有个then方法
new Promise((resolve) => {
console.log(1)
resolve()
}).then(() => {
console.log(2)
return {
then (resolve, reject) {
console.log(3);
resolve(4)
}
}
}).then(res => {
console.log(res)
})
打印结果
1
2
3
4
thenable 不执行resolve()
如果 thenable 里面没有执行 resolve(),那么这个promise后面的.then方法将不执行
new Promise((resolve) => {
console.log(1)
resolve()
}).then(() => {
console.log(2)
return {
then () {
console.log(3);
}
}
}).then(res => { // 注意:此then不执行
console.log(res)
})
打印结果
1
2
3
返回 Promise对象
返回一个未定状态(pending
)的 Promise
返回一个未定状态(pending
)的 Promise,那么 then
返回 Promise 的状态也是未定的
new Promise((resolve) => {
console.log(1)
resolve()
}).then(() => {
console.log(2)
return new Promise(() => {})
}).then(res => {
console.log(res)
})
打印结果
1
2
返回一个已经是接受状态的 Promise
返回一个已经是接受状态的 Promise,那么 then
返回的 Promise 也会成为接受状态
并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。
new Promise((resolve) => {
console.log(1)
resolve()
}).then(() => {
console.log(2)
return new Promise((resolve, reject) => {
resolve('success')
})
}).then(res => {
console.log(res) // success
})
打印结果
1
2
success
返回一个已经是拒绝状态的 Promise
返回一个已经是拒绝状态的 Promise,那么 then
返回的 Promise 也会成为拒绝状态,并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。
new Promise((resolve) => {
console.log(1)
resolve()
}).then(() => {
console.log(2)
return new Promise((resolve, reject) => {
reject('error')
})
}).then(res => {
console.log(res) // Uncaught (in promise) error
})
打印结果
1
2
Uncaught (in promise) error
抛出一个错误
抛出一个错误,那么 then
返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。
new Promise((resolve) => {
console.log(1)
resolve()
}).then(() => {
console.log(2)
throw new Error('出错了')
}).then(res => {
console.log(res)
}, err => {
console.log(err); // Error: 出错了
})
打印结果
1
2
Error: 出错了
onRejected
onRejected:失败回调 (err) => {}
当 Promise 变成拒绝状态(rejected)时调用的函数
。该函数有一个参数,即拒绝的原因(rejection reason
)。如果该参数不是函数,则会在内部被替换为一个 “Thrower” 函数 (it throws an error it received as argument)。
onRejected 与 catch 优先级
new Promise((resolve, reject) => {
reject(new Error('11'))
}).then(res => {
console.log('then res:', res)
}, err => {
console.log('then err:', err)
}).catch(err => {
console.log('catch err:', err)
}).finally(() => {
console.log('finally', res)
})
执行结果:
结论:
-
then 的错误回调和catch同时存在,then的错误回调 优先级大于 catch。
-
当不存在then的错误回调时,才会走catch
链式操作
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("异步 11");
resolve("success");
}, 1000);
});
const p2 = p.then((res) => {
console.log("异步完成", res);
}).then((res) => {
console.log("异步完成1", res);
}).then((res) => {
console.log("异步完成2", res);
});
console.log(p2);
p.then().then()
和 p.then() p.then()
的区别
promise内都是同步代码看不出区别来,如果是异步代码就可以看出区别
then 中同步代码
首先看下 p.then() p.then()
:
let p = new Promise((resolve) => {
console.log(1)
resolve()
})
p.then(() => {
console.log(2)
})
p.then(res => {
console.log(3)
})
打印结果:
1
2
3
再看下 p.then().then()
:
let p = new Promise((resolve) => {
console.log(1)
resolve()
})
p.then(() => {
console.log(2)
}).then(res => {
console.log(3)
})
打印结果:
1
2
3
上面都是同步代码,从打印结果上看不出任何区别,下面在then中使用异步代码进行测试
then 中异步代码
还是先看 p.then() p.then()
:
let p = new Promise((resolve) => {
console.log(1)
resolve()
})
p.then(() => {
console.log(2)
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('2-1');
resolve()
}, 1000);
})
})
p.then(res => {
console.log(3)
})
打印结果
1
2
3
2-1 // 过一秒出来
再看 p.then().then()
:
let p = new Promise((resolve) => {
console.log(1)
resolve()
})
p.then(() => {
console.log(2)
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('2-1');
resolve()
}, 1000);
})
}).then(res => {
console.log(3)
})
打印结果
1
2
2-1 // 过一秒出来
3
结论
then中是同步代码那么 p.then().then()
链式调用跟 p.then() p.then()
执行结果一致。
then中是异步代码,两者的执行结果不一致,所以两者看似没有区别,实际上是有很大区别的,这是为什么?
分析:
p = new promise()
可以看成发布订阅(发布任务),resolve
相当于 notify 任务结束通知
then
可以看成监听 addEventListener
p.then() p.then()
可以看成是对这个 p
这个对象添加了2个观察者,一旦监听被触发,所有的观察者都会同时执行
p.then().then()
相当于一个函数调完后有返回值,另一个函数接着上一个函数执行
补充:p.then().then()
可以等同于如下
let p = new Promise((resolve) => {
console.log(1)
resolve()
})
p.then(() => {
console.log(2)
}).then(res => {
console.log(3)
})
// p.then().then() 等同于如下
let p2 = p.then(() => {
console.log(2)
})
p2.then(res => {
console.log(3)
})
Promise.prototype.catch
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("异步 11");
resolve("success");
}, 1000);
});
const p2 = p.then((res) => {
console.log("异步完成", res);
}).catch(err => {
console.log(err)
}).then(res => {
console.log(res)
});
console.log(p2);
参数:参照 then
由于catch返回值与then一样,返回值为 promise对象,所以catch 之后还可以then
Promise.prototype.finally
【ES2018】
finally()
方法返回一个Promise
。在promise结束时,无论结果是 fulfilled
或者是 rejected
,都会执行指定的回调函数。这为在Promise
是否成功完成后都需要执行的代码提供了一种方式。
这避免了同样的语句需要在then()
和catch()
中各写一次的情况。
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("异步 11");
// resolve("success");
reject('error')
}, 1000);
});
p.then(res=>{
console.log(res);
},err=>{
console.log(err);
}).finally(()=>{
console.log("执行完成");
})
success情况:
error情况:
Promise 静态方法
Promise.resolve()
返还一个 状态为 resolved 的 promise对象
const p = Promise.resolve(() => {
setTimeout(() => {
console.log('111')
}, 1000)
})
console.log(p)
Promise.reject()
返还一个 状态为 rejected 的 promise对象
const p = Promise.reject(() => {
setTimeout(() => {
console.log('111')
}, 1000)
})
console.log(p)
Promise.all()
同步执行所有的Promise
-
所有的 promise 都成功才成功,返回 存放所有 resolve值的数组
-
有一个失败即算失败,返回第一个错误结果,其他Promise会执行,但是执行结果不返回
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("111");
resolve(1);
}, 1500);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("222");
resolve(2);
}, 2000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("333");
resolve(3);
}, 1000);
})
Promise.all([p1, p2, p3]).then((res) => {
console.log('res: ', res)
})
注意:不管 Promise.all 执不执行,p1 p2 p3 里面的异步都会执行
-
存在reject
const p1 = new Promise((resolve, reject) => { setTimeout(() => { console.log("111"); reject(1); }, 1500); }) const p2 = new Promise((resolve, reject) => { setTimeout(() => { console.log("222"); reject(2); }, 2000); }) const p3 = new Promise((resolve, reject) => { setTimeout(() => { console.log("333"); resolve(3); }, 1000); }) Promise.all([p1, p2, p3]).then((res) => { console.log('res: ', res) }, err => { console.log('err:', err) })
Promise.allSettled() 【ES2020】
同步执行所有的Promise,不管是成功还是失败都会返回 {status, reason | value } 的对象数组
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("111");
reject(1);
}, 1500);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("222");
resolve(2);
}, 2000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("333");
resolve(3);
}, 1000);
})
Promise.allSettled([p1, p2, p3]).then((res) => {
console.log('res: ', res)
})
Promise.race()
先执行完成的先返回,无论成功还是失败只会返回第一个执行完毕的结果
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("111");
resolve(1);
}, 1500);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("222");
resolve(2);
}, 2000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("333");
resolve(3);
}, 1000);
})
Promise.race([p1, p2, p3]).then((res) => {
console.log('res: ', res)
}, err => {
console.log('err:', err)
})
假设最快执行完毕的是reject,则返回reject
Promise.any() 【ES2021】
同步执行所有的Promise
- 所有失败才算失败,返回 存放所有 error 的数组
- 有一个成功就算成功,返回最快resolve的结果
所有都是reject:
const promises = [
Promise.reject('ERROR A'),
Promise.reject('ERROR B'),
Promise.reject('ERROR C'),
]
Promise.any(promises).then((value) => {
console.log('value:', value)
}).catch((err) => {
console.log('err:', err)
console.log(err.message)
console.log(err.name)
console.log(err.errors)
})
// err:AggregateError: All promises were rejected
// All promises were rejected
// AggregateError
// ["ERROR A", "ERROR B", "ERROR C"]
有一个resolve:
const promises = [
Promise.reject('ERROR A'),
Promise.reject('ERROR B'),
Promise.resolve('result'),
]
Promise.any(promises).then((value) => {
console.log('value: ', value)
}).catch((err) => {
console.log('err: ', err)
})
// value: result
Promise 问题
永不 resolve/reject 的 Promise 会导致内存泄漏吗?
参考:
https://zhuanlan.zhihu.com/p/385764204?ivk_sa=1024320u
https://www.shuzhiduo.com/A/xl56Ae94Jr/
https://www.zhihu.com/question/386595851
跟内存泄漏没有直接关系
gc 的策略不会改变,如果该 promise 没有被人引用,就会被 gc 掉。如果仍被引用,就不会被 gc 掉。
即使一个 promise,resolve 或者 reject 了,但是它还被人引用,仍然占用内存。
结论:
- 未执行完成的
Promise
(包括内部等待的回调未完成),并被引用着,会占用内存。 - 执行完成的
Promise
(包括内部等待的回调也执行完成),不占用内存,可被GC
释放。 - 如果多次使用同一个
new Promise
,且没有resolve
/reject
,不会被垃圾回收。 - 如果每次都生成新的
new Promise
,且没有resolve
/reject
,直接被GC
释放。
GC
:垃圾收集
如果使用同一个 new Promise
,且没有 resolve
/ reject
,不会被垃圾回收
const pendingPromise = new Promise(() => {})
const dummyGet1 = () => {
return pendingPromise
}
const next = (res) => {
console.log(res)
}
// pending 1000, 一直存在,没被回收
// 需要使用.then,不使用then会被垃圾回收
Array(1000)
.fill(1)
.forEach(() => dummyGet1().then(next))
作者:蔡锦
链接:https://www.zhihu.com/question/386595851/answer/1270721798
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
如果每次都生成新的 new Promise
且没有 resolve
/ reject
,直接被垃圾回收
// 如果 resolve 没有被调用,那么就直接被回收。
const dummyGet2 = () => {
return new Promise(() => {})
}
const next = (res) => {
console.log(res)
}
// 被回收了
Array(1000)
.fill(1)
.forEach(() => dummyGet2().then(next))
作者:蔡锦
链接:https://www.zhihu.com/question/386595851/answer/1270721798
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
练一练
考验 promise.then(2) 的结果
// 给出一个 promise
var promise = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(0)
}, 3000)
})
// 请问以下几种有何不同?
// 1
promise
.then(() => {
return Promise.resolve(1)
})
.then((n) => {
console.log(n)
})
// 2
promise
.then(() => {
return 2
})
.then((n) => {
console.log(n)
})
// 3
promise.then(3).then((n) => {
console.log(n)
})
// 4
promise
.then(() => {
return 4
})
.then(5)
.then((n) => {
console.log(n)
})
答案:2 0 4 1
1 => 1
2 => 2
3 => 0
4 => 4
说出下列代码执行结果?
let a
const b = new Promise((resolve, reject) => {
console.log('promise1')
resolve()
})
.then(() => {
console.log('promise2')
})
.then(() => {
console.log('promise3')
})
.then(() => {
console.log('promise4')
})
a = new Promise(async (resolve, reject) => {
console.log(a)
await b
console.log(a)
console.log('after1')
await a
resolve(true)
console.log('after2')
})
console.log('end')
答案:
promise1
undefined
end
promise2
promise3
promise4
Promise { [[PromiseState]]: "pending", [[PromiseResult]]: undefined }
after1
解析: 可以将 then 改写成 async/await 来看 ,如下代码:
a = new Promise((resolve, reject) => {
console.log(a) //undefined
return Promise.resolve(b).then((res) => {
console.log(a)
console.log('after1')
return Promise.resolve(a).then((res) => {
console.log('after2')
resolve(true)
})
})
})
然后先拿出同步执行的代码 ,同步执行代码有: console.log('promise1');
console.log(a);
console.log('end');
由于在打印的时候a
还没有被赋值,所以得到打印undefined
。 故先得到打印 promise1 undefined end
接下来在来看异步代码执行 ,先会执行 Promise.resolve(b)
,得到打印console.log('promise2');
console.log('promise3');
console.log('promise4');
,
之后执行返还 promise
里 then
里的内容 由于是异步,故 a
已经完成了赋值操作,但是还未执行到resolve
调用,所以只能得到 pending
状态的promise
对象。得到打印 Promise {}
然后打印 after1
由于 Promise.resolve(a)
的then
执行需要再 a
promise
调取 resolve
之后执行,此时 a
的 promise
并没有调用 resolve
所以得不到 then
里的打印 。
综上所述:打印结果是: promise1--> undefined --> end-->promise2---> promise3--->promise4--->Promise {} --->after1
请说出以下执行结果?
function test() {
console.log(1)
setTimeout(function () {
// timer1
console.log(2)
}, 1000)
}
test()
setTimeout(function () {
// timer2
console.log(3)
})
new Promise(function (resolve) {
console.log(4)
setTimeout(function () {
// timer3
console.log(5)
}, 100)
resolve() // 考察点在这个
}).then(function () {
setTimeout(function () {
// timer4
console.log(6)
}, 0)
console.log(7)
})
console.log(8)
执行结果:
1
4
8
7
3
6
5
2
按要求完成 mergePromise 代码
const timeout = (ms) =>
new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, ms)
})
const ajax1 = () =>
timeout(2000).then(() => {
console.log('1')
return 1
})
const ajax2 = () =>
timeout(1000).then(() => {
console.log('2')
return 2
})
const ajax3 = () =>
timeout(2000).then(() => {
console.log('3')
return 3
})
const mergePromise = (ajaxArray) => {
// 1,2,3 done [1,2,3]
}
mergePromise([ajax1, ajax2, ajax3]).then((data) => {
console.log('done')
console.log(data) // data 为[1,2,3]
})
// 要求执行结果为:1 2 3 done [1,2,3]
答案 :
- 递归
const mergePromise = (ajaxArray) => {
// 1,2,3 done [1,2,3]
return new Promise((resolve, reject) => {
let num = 0
let resArr = []
fn()
function fn() {
if (num === ajaxArray.length) {
resolve(resArr)
return
}
ajaxArray[num]().then((res) => {
num++
resArr.push(res)
fn()
})
}
})
}
- 利用 async 及 await 来实现
const mergePromise = (ajaxArray) => {
// 1,2,3 done [1,2,3]
return new Promise(async (resolve, reject) => {
let resArr = []
for (let i = 0; i < ajaxArray.length; i++) {
let res = await ajaxArray[i]()
resArr.push(res)
}
resolve(resArr)
})
}
请写出以下输出的顺序,并指出 js 中微任务和宏任务有哪些。
console.log('start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
new Promise((resolve) => {
console.log('promise')
resolve()
})
.then(() => {
console.log('then1')
})
.then(() => {
console.log('then2')
})
console.log('end')
答案:
-
输出顺序:先执行同步,后执行微任务,最后再执行宏任务
start promise end then1 then2 setTimeout
-
宏任务
setTimeout(() => { console.log('setTimeout') }, 0)
-
微任务
.then(() => { console.log('then1') }).then(() => { console.log('then2') })
尝试手写一个 Promise
涉及相关面试题:
-
什么是微任务和宏任务
-
Promise
如何实现?- 至少要把 3 种状态、
Promise.then
写出来
- 至少要把 3 种状态、
-
Promise.all
、Promise.allSetlled
… 原理写出来 -
handle(this.#resolve.bind(this), this.#reject.bind(this))
为什么需要重新设置 this 指向