Open luckyyyyy opened 7 years ago
Promise 作为 ECMAScript 6 的异步规范,为我们解决了很多异步回调问题,Ajax 出现的时候,刮来了一阵异步之风,现在 Nodejs 火爆,又一阵异步狂风刮了过来。需求是越来越苛刻,用户对性能的要求也是越来越高,随之而来的是页面异步操作指数般增长,如果不能恰当的控制代码逻辑,我们就会陷入无穷的回调地狱中。
由于 JavaScript 的单线程性质,我们必须等待上一个事件执行完成才能处理下一步,下面是一个很经典的例子。
$(function() { // 获取模板 $.get(url, function(tpl) { // 获取数据 $.get(url2, function(data) { // 构建 DOMString makeHtml(tpl, data, function(str) { // 插入到 DOM 中 $(obj).html(str); }); }); }); });
上面是一个很经典的例子,为了首屏快速加载,我们会把模板放在服务器,当用户获取数据时,我们会拉取模板并且拉取数据,最终拼接起来插入DOM中,但这就可能陷入了无限的回调地狱中,代码看起来既不美观又容易扰乱我们的思维,下面我们就用Promise来解决上面的问题。
// 伪代码 new Promise(ready).then(getTpl).then(getData).then(makeHtml).resolve();
如果变成上面这样,是不是就清晰多了?不过的确使用Promise上面的代码可以成立,那么我们就来详细介绍一下。
在 Promise 之前,其实我们早已迎来的 jQuery 的 Deferred,当然它与 Promise 并不兼容,具体可以查看这里。
// 创建一个promise对象 const promise = new Promise((resolve, reject) => { // do a thing, possibly async, then… if (/* everything turned out fine */) { resolve("Stuff worked!"); } else { reject(Error("It broke")); } });
Promise 构造函数包含一个参数和一个带有 resolve(解析)和 reject(拒绝)两个参数的回调。在回调中执行一些操作(例如异步),如果一切都正常,则调用 resolve,否则调用 reject,与普通旧版 JavaScript 中的 throw 一样,通常拒绝时会给出 Error 对象,但这不是必须的。Error 对象的优点在于它们能够捕捉堆追踪,因而使得调试工具非常有用。
resolve
reject
// 使用刚才创建的promise对象 promise.then(result => { console.log(result); // "Stuff worked!" }, err => { console.log(err); // Error: "It broke" }); promise.then(result => { console.log(result); // "Stuff worked!" }).catch(err => { console.log(err); // Error: "It broke" });
then() 包含两个参数:一个用于成功情形的回调和一个用于失败情形的回调。这两个都是可选的,因此您可以只添加一个用于成功情形或失败情形的回调。 catch() 没有任何特殊之处,它只是 then(undefined, func) 的锦上添花,但可读性更强。注意,以上两个代码示例行为并不相同,后者相当于:
then()
catch()
promise.then(result => { console.log(result); // "Stuff worked!" }).then((undefined, err) => { console.log(err); // Error: "It broke" })
两者之间的差异虽然很微小,但非常有用。Promise 拒绝后,将跳至带有拒绝回调的下一个 then()(或具有相同功能的 catch())。如果是 then(func1, func2),则 func1 或 func2 中的一个将被调用,而不会二者均被调用。但如果是 then(func1).catch(func2),则在 func1 拒绝时两者均被调用,因为它们在该链中是单独的步骤。
JavaScript promise 最初是在 DOM 中出现并称为Futures,之后重命名为Promises,最后又移入 JavaScript。在 JavaScript 中使用比在 DOM 中更好,因为它们将在如 Node.js 等非浏览器 JS 环境中可用(而它们是否会在核心 API 中使用 Promise 则是另外一个问题)。
Futures
Promises
尽管它们是 JavaScript 的一项功能,但 DOM 也能使用。实际上,采用异步成功/失败方法的所有新 DOM API 均使用 promise。
现在前端的兼容性问题在babel出现后已经不存在较大的问题了,对于这种API,也基本用一种叫polyfill(垫片)的技术,当然 Promise 也有其响应的polyfill。
babel
polyfill
下面列出已经原生支持的浏览器,IE全版本全军覆没。
new Promise((resolve, reject) => {}); // resolve(thenable) // Promise 依据 thenable 的结果而执行/拒绝。 // resolve(obj) // Promise 执行并返回 obj // reject(obj) // Promise 拒绝并返回 obj。为保持一致和调试(例如堆叠追踪),obj 应为 instanceof Error。 在构造函数回调中引发的任何错误将隐式传递给 reject()。
promise.then(onFulfilled, onRejected) // 当/如果“promise”解析,则调用 onFulfilled。当/如果“promise”拒绝,则调用 onRejected。 两者均可选,如果任意一个或两者都被忽略,则调用链中的下一个 onFulfilled/onRejected。 两个回调都只有一个参数:执行值或拒绝原因。 then() 将返回一个新 promise,它相当于从 onFulfilled/onRejected 中返回、 通过 Promise.resolve 传递的值。如果在回调中引发了错误,返回的 promise 将拒绝并返回该错误。 promise.catch(onRejected) // promise.then(undefined, onRejected)
ES6 还为我们提供了 generator,它可让某些功能在某个位置退出(类似于return),但之后能以相同位置和状态恢复,例如:
return
function *addGenerator() { let i = 0; while (true) { i += yield i; } }
注意函数名称前面的星号,这表示 generator。yield 关键字是我们的返回/恢复位置。我们可按下述方式使用:
const adder = addGenerator(); adder.next().value; // 0 adder.next(5).value; // 5 adder.next(5).value; // 10 adder.next(5).value; // 15 adder.next(50).value; // 65
更深入的不在这里讲解,generator其实存在诸多问题,所以在ES7中,我们有了更好的方式,async和await,等下一篇详解。
async
await
Auout Promise
Promise 作为 ECMAScript 6 的异步规范,为我们解决了很多异步回调问题,Ajax 出现的时候,刮来了一阵异步之风,现在 Nodejs 火爆,又一阵异步狂风刮了过来。需求是越来越苛刻,用户对性能的要求也是越来越高,随之而来的是页面异步操作指数般增长,如果不能恰当的控制代码逻辑,我们就会陷入无穷的回调地狱中。
由于 JavaScript 的单线程性质,我们必须等待上一个事件执行完成才能处理下一步,下面是一个很经典的例子。
上面是一个很经典的例子,为了首屏快速加载,我们会把模板放在服务器,当用户获取数据时,我们会拉取模板并且拉取数据,最终拼接起来插入DOM中,但这就可能陷入了无限的回调地狱中,代码看起来既不美观又容易扰乱我们的思维,下面我们就用Promise来解决上面的问题。
如果变成上面这样,是不是就清晰多了?不过的确使用Promise上面的代码可以成立,那么我们就来详细介绍一下。
Deferred & Promise
在 Promise 之前,其实我们早已迎来的 jQuery 的 Deferred,当然它与 Promise 并不兼容,具体可以查看这里。
创建 Promise 对象
Promise 构造函数包含一个参数和一个带有
resolve
(解析)和reject
(拒绝)两个参数的回调。在回调中执行一些操作(例如异步),如果一切都正常,则调用 resolve,否则调用 reject,与普通旧版 JavaScript 中的 throw 一样,通常拒绝时会给出 Error 对象,但这不是必须的。Error 对象的优点在于它们能够捕捉堆追踪,因而使得调试工具非常有用。then()
包含两个参数:一个用于成功情形的回调和一个用于失败情形的回调。这两个都是可选的,因此您可以只添加一个用于成功情形或失败情形的回调。catch()
没有任何特殊之处,它只是 then(undefined, func) 的锦上添花,但可读性更强。注意,以上两个代码示例行为并不相同,后者相当于:两者之间的差异虽然很微小,但非常有用。Promise 拒绝后,将跳至带有拒绝回调的下一个 then()(或具有相同功能的 catch())。如果是 then(func1, func2),则 func1 或 func2 中的一个将被调用,而不会二者均被调用。但如果是 then(func1).catch(func2),则在 func1 拒绝时两者均被调用,因为它们在该链中是单独的步骤。
JavaScript promise 最初是在 DOM 中出现并称为
Futures
,之后重命名为Promises
,最后又移入 JavaScript。在 JavaScript 中使用比在 DOM 中更好,因为它们将在如 Node.js 等非浏览器 JS 环境中可用(而它们是否会在核心 API 中使用 Promise 则是另外一个问题)。尽管它们是 JavaScript 的一项功能,但 DOM 也能使用。实际上,采用异步成功/失败方法的所有新 DOM API 均使用 promise。
兼容性问题
现在前端的兼容性问题在
babel
出现后已经不存在较大的问题了,对于这种API,也基本用一种叫polyfill
(垫片)的技术,当然 Promise 也有其响应的polyfill。下面列出已经原生支持的浏览器,IE全版本全军覆没。
Promise API 参考
then()
方法的类似于 Promise 的对象。构造函数
实例方法
generator
ES6 还为我们提供了 generator,它可让某些功能在某个位置退出(类似于
return
),但之后能以相同位置和状态恢复,例如:注意函数名称前面的星号,这表示 generator。yield 关键字是我们的返回/恢复位置。我们可按下述方式使用:
更深入的不在这里讲解,generator其实存在诸多问题,所以在ES7中,我们有了更好的方式,
async
和await
,等下一篇详解。