难点一:promise的状态
promise是一个容器,它具有三种状态<pending>(进行中)<fulfilled>(成功)<rejected>(失败)
promise创建时即执行,此时状态为pending,当碰到resolve()语句时状态变为fulfilled,碰到reject()语句或内部出现错误时状态变为rejected。当promise状态确定为成功或失败后,promise状态不会再改变。例如
let p = new Promise((resolve, reject) => {
resolve()
reject()
})
setTimeout(() => {
console.log(p)
}, 0);
输出为Promise {<fulfilled>: undefined},promise先执行resolve()导致状态变为成功,此时状态确定,后续的reject()不会使状态发生改变
注意:虽然promise状态确定后不会再改变,但是resolve()和reject()后的代码还是会执行
如果
难点二:promise在JS事件中的执行顺序
JS作为一门单线程语言,一次只会执行一个线程。线程中的任务分为宏任务和微任务。宏任务即setTimeOut和各种事件之类的任务,微任务即promise,process.nextTick、Object.observe(已废弃)、 MutationObserver几种。开始时JS执行同步任务,同时将出现的微任务加入微任务队列,将宏任务加入宏任务队列。执行完同步任务后检查微任务队列,依次执行微任务,执行完毕后进行下一个宏任务事件。执行下个宏任务事件时将当前宏任务事件中出现的微任务加入微任务队列,出现的宏任务加入宏任务队列,如上循环操作,直至队列为空。以下面代码为例
new Promise(resolve => {
console.log('start')
resolve()
}).then(()=>{
console.log('Promise1')
setTimeout(()=>{
console.log('setTimeout2')
},0)
})
setTimeout(()=>{
console.log('setTimeout1')
Promise.resolve().then(()=>{
console.log('Promise2')
})
},0)
console.log('end')
JS首先执行同步任务(注意:生成promise的代码属于同步代码,.then.catch之后的代码才是加入微任务的异步代码)输出start和end,同时将promise1的then加入微任务队列,将settimeout1加入宏任务队列。同步代码完成后,检测到微任务队列中的promise1.then,执行后输出promise1,同时将settimeout2加入宏任务队列。执行完微任务队列后,进行下个宏任务,即settimeout1。输入settimeout1中的同步代码settimeout1,将promise2加入微任务队列。settimeout2完成后,执行此时微任务队列中的promise2。微任务队列执行完成后进入下个宏任务即settimeout2,输出settimeout2。此时,所有队列都为空,任务完成。
输出顺序为start;end;Promise1 ;setTimeout1;Promise2 ;setTimeout2
难点三:promise中的resolve
Promise.resolve('foo')/ 等价于newPromise(resolve=>resolve('foo'))
其中resolve的参数可分为四种类型
(1)参数是一个 Promise 实例
如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。注意下面这段代码
const p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})
p2
.then(result => console.log(result))
.catch(error => console.log(error))
一秒之后P2执行resolve(P1),如我们上面所说,此时P2将原封不动返回P1,后续的回调也将变成P1的回调,3秒后P1状态变为失败,触发catch回调输出Error: fail。
注意:如果P1的状态比P2先发生变化,在node环境下会多输出UnhandledPromiseRejectionWarning: Error: fail语句,原因是node会自动捕获没有catch回调时promise产生的错误,此时P2状态已经变化但还没有自己的错误处理函数,所以出现这条语句
(2)参数是一个thenable对象
thenable对象指的是具有then方法的对象,Promise.resolve()方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function (value) {
console.log(value); // 42
});
上面代码中,thenable对象的then()方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then()方法指定的回调函数,输出42。
(3)参数不是具有then()方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then()方法的对象,则Promise.resolve()方法返回一个新的 Promise 对象,状态为resolved。
const p=Promise.resolve('Hello');
p.then(function(s){console.log(s)});// Hello
上面代码生成一个新的 Promise 对象的实例p。由于字符串Hello不属于异步操作(判断方法是字符串对象不具有 then 方法),返回 Promise 实例的状态从一生成就是resolved,所以回调函数会立即执行。Promise.resolve()方法的参数,会同时传给回调函数。
(4)不带有任何参数
Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
网友评论