Open evantianx opened 6 years ago
实例化后的 Promise 对象有三个状态:
"has-resolution" Fulfilled
resolve。此时会调用 onFulfilled
。
"has-rejection" Rejected
reject。此时会调用 onRejected
。
"has-resolved" Pending
既不是 resolve 也不是 reject 的状态。即 promise 对象刚被创建后的初始化状态。
[[PromiseStatus]] 都是在内部定义的状态。没有公开 API。
状态从 Pending 转换为 Fulfilled 或者 Rejected 后将不再发生任何变化。 这也意味着在
.then
后执行的函数只会执行一次
function getURL(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.onload = () => {
if (req.status === 200) {
resolve(req.responseText)
} else {
reject(new Error(req.statusText))
}
}
xhr.onerror = () => {
reject(new Error(req.statusText))
}
xhr.send()
})
}
是的。
为何?
普遍存在的问题:回调函数中存在同步异步混合调用
const onReady = fn => {
const readyState = document.readyState
if (readyState === 'interactive' || readyState === 'complete') {
fn()
} else {
window.addEventListener('DOMContentLoaded', fn)
}
}
onReady(() => console.log('DOM fully loaded and parsed'))
console.log('----start here----')
上面的代码中出现了在一种情况下回调函数是同步调用,另外一种情况是异步调用。这样会带来很大的混乱,导致开发者完全不知道代码执行顺序会是怎样。
const onReady = fn => {
const readyState = document.readyState
if (readyState === 'interactive' || readyState === 'complete') {
setTimeout(fn, 0)
} else {
window.addEventListener('DOMContentLoaded', fn)
}
}
onReady(() => console.log('DOM fully loaded and parsed'))
console.log('----start here----')
这样修改之后,开发者对于执行流程就会非常明确,回调函数始终是异步调用。
基于异步同步调用混乱问题, Promise 中只存在异步调用:
const onReady = () => {
return new Promise((resolve, reject) => {
const readyState = document.readyState
if (readyState === 'interactive' || readyState === 'complete') {
resolve()
} else {
window.addEventListener('DOMContentLoaded', resolve)
}
})
}
onReady()
.then(() => console.log('DOM fully loaded and parsed'))
console.log('----start here----')
由于 IE8 不支持 Promise,所以需要利用 polyfill 来模拟实现。
在某些 Promise 实现中,直接使用 promise.catch()
类似的语句会出现 identifier not found 的错误,这是由于catch 属于保留字,而在 ECMAScript3 中保留字是不可以当作属性名使用的。IE8 及以下版本都是基于 ECMAScript3 实现的。
如何避免?
使用中括号标记法则可以使用非合法标志符:
// IE8 下使用 Promise polyfill
var promise = Promise.reject(new Error('message'))
promise['catch'](function(error) {
console.error(error)
})
或者用 promise.then()
代替:
var promise = Promise.reject(new Error('message'))
promise.then(undefined, function(error) {
console.error(error)
})
目前很多压缩工具自带了将
promise.catch
转换为promise['catch']
的功能,帮我们解决了这个问题。
promise#then
or promise#catch
我们在处理错误时,通常会有两种写法:
promise.then(onFulfilled, onRejected)
promise.then(onFulfilled).catch(onRejected)
使用 promise.then(onFulfilled, onRejected)
的话
若在 onFulfilled
中发生异常,在 onRejected
中是捕获不到这个异常的。
在 promise.then(onFulfilled).catch(onRejected)
的话
then
中产生的异常能在 .catch
中捕获
.then
和 .catch
在本质上是没有区别的
需要分场合使用。
// await 实现
const getBook = async bookName => {
const book = await fetchBook(bookName);
const author = await fetchAuthor(book.authorId);
const rating = await fetchRating(book.id);
return {
...book,
author,
rating
};
};
getBook("The Wealth of Nations");
// promise.all + async/await 实现
const getBook = async bookName => {
const book = await fetchBook(bookName);
return Promise.all([
fetchAuthor(book.authorId),
fetchRating(book.id)
]).then(results => ({
...book,
author: results[0],
rating: results[1]
}));
};
// promise.all + async/await 实现最终版
const getBook = async bookName => {
const book = await fetchBook(bookName);
const [author, rating] = await Promise.all([
fetchAuthor(book.authorId),
fetchRating(book.id)
]);
return {
...book,
author,
rating
};
};
参考:
Constructor
ES6 中,使用
Promise()
构造函数可以创建一个 Promise 对象。实例方法
更推荐 :
Promise#then
不仅仅是注册一个回调函数,它还负责将回调函数返回值包装成一个 promise 对象静态方法
Promise.all
接受一个 promise 对象的作为参数,只有这个数组里所有 promise 对象全部变成 resolve 或者 reject 的时候才会去调用
.then
方法。Promise.race()
同样接受一个 promise 对象数组,区别于
promise.all
的是,当其中一个 promise 对象进入 Fulfilled 或者 rejected 状态的话,就会继续进行后面的处理Promise.resolve()
两种用途:
相当于
new Promise()
的一个快捷方式。如
等价于:
用于将 thenable 对象转换为 promise 对象