资讯专栏INFORMATION COLUMN

JavaScript Promises 初体验

Baoyuan / 1087人阅读

摘要:回调函数是的一大特色官方的基本都是以会回调方式传递函数返回值。针对这种普遍问题,应势而生基本用法创建做一些异步操作的事情,然后一切正常的构造器接受一个函数作为参数,它会传递给这个回调函数两个变量和。

Promise 是什么?

Promise 对象用来进行延迟(deferred) 和 异步(asynchronous) 计算。

一个 Promise 处于以下三种状态之一:

pending: 初始状态, 非 fulfilled 或 rejected.

fulfilled: 成功的操作.

rejected: 失败的操作.

Promise 接口表示为一个值的代理,这个值在promise创建时未必已知. 它允许你将 handlers 与一个异步 action 最终的成功或失败状态关联起来. 这使得异步方法可以像同步方法那样返回值: 异步方法返回一个在未来某个时刻拥有一个值的 promise 来替代最终返回值.

pending状态的promise既可带着一个值成为 fulfilled 状态,也可带着一个reason成为 rejected 状态. 任意情况发生时, 通过promise的 then 方法所排列(queued up)的相应handlers 就会被调用. (当绑定相应的 handler 时,如果 promise 已经处于 fulfilled 或 rejected 状态这个 handler 将会被调用, 所以在异步操作的完成和它的 handler 的绑定之间不存在竞争条件.)

因为 Promise.prototype.then 和 Promise.prototype.catch 方法返回 promises, 所以它们可以被链式调用—一种被称为 composition 的操作.

Promise 为何出现?

简单的说,Promise 被当成 callback hell 的救命仙丹。

回调函数是JavaScript的一大特色! node.js官方的api基本都是以会回调方式传递函数返回值。习惯同步编程的对这种异步方式多少会产生水土不服,而且层层嵌套,不知不觉就建造起了一座高高的回调金字塔。针对这种普遍问题,Promise应势而生!

Promise 基本用法

创建 Promise

var promise = new Promise(function(resolve, reject) {
  // 做一些异步操作的事情,然后……

  if (/* 一切正常 */) {
    resolve("Stuff worked!");
  }
  else {
    reject(Error("It broke"));
  }
});

Promise 的构造器接受一个函数作为参数,它会传递给这个回调函数两个变量 resolve 和 reject。在回调函数中做一些异步操作,成功之后调用 resolve,否则调用 reject。

调用 reject 的时候传递给它一个 Error 对象只是个惯例并非必须,这和经典 JavaScript 中的 throw 一样。传递 Error 对象的好处是它包含了调用堆栈,在调试的时候会有点用处。

使用 Promise:

promise.then(function(result) {
  console.log(result); // “完美!”
}, function(err) {
  console.log(err); // Error: "出问题了"
});

“then”接受两个参数,成功的时候调用一个,失败的时候调用另一个,两个都是可选的,所以你可以只处理成功的情况或者失败的情况。

Promise 实践

值的处理

你可以对结果做些修改然后返回一个新值:

var promise = new Promise(function(resolve, reject) {
  resolve(1);
});

promise.then(function(val) {
  console.log(val); // 1
  return val + 2;
}).then(function(val) {
  console.log(val); // 3
});

队列的异步操作

你也可以把“then”串联起来依次执行异步操作。

当你从“then”的回调函数返回的时候,这里有点小魔法。如果你返回一个值,它就会被传给下一个“then”的回调;而如果你返回一个“类 Promise”的对象,则下一个“then”就会等待这个 Promise 明确结束(成功/失败)才会执行。例如:

你也可以把“then”串联起来依次执行异步操作。

当你从“then”的回调函数返回的时候,这里有点小魔法。如果你返回一个值,它就会被传给下一个“then”的回调;而如果你返回一个“类 Promise”的对象,则下一个“then”就会等待这个 Promise 明确结束(成功/失败)才会执行。例如:

返回一个值

getJSON("story.json").then(function(story) {
  return getJSON(story.chapterUrls[0]);
}).then(function(chapter1) {
  console.log("Got chapter 1!", chapter1);
});

你返回一个“类 Promise”的对象

var storyPromise;

function getChapter(i) {
  storyPromise = storyPromise || getJSON("story.json");

  return storyPromise.then(function(story) {
    return getJSON(story.chapterUrls[i]);
  })
}

// 用起来非常简单:
getChapter(0).then(function(chapter) {
  console.log(chapter);
  return getChapter(1);
}).then(function(chapter) {
  console.log(chapter);
});

错误处理

前面已经看到,“then”接受两个参数,一个处理成功,一个处理失败(或者说肯定和否定,按 Promise 术语):

get("story.json").then(function(response) {
  console.log("Success!", response);
}, function(error) {
  console.log("Failed!", error);
});

你还可以使用“catch”:

get("story.json").then(function(response) {
  console.log("Success!", response);
}).catch(function(error) {
  console.log("Failed!", error);
});

Promise 被否定之后会跳转到之后第一个配置了否定回调的 then(或 catch,一样的)。对于 then(func1, func2) 来说,必会调用 func1 或 func2 之一,但绝不会两个都调用。而 then(func1).catch(func2) 这样,如果 func1 返回否定的话 func2 也会被调用,因为他们是链式调用中独立的两个步骤。看下面这段代码:

asyncThing1().then(function() {
  return asyncThing2();
}).then(function() {
  return asyncThing3();
}).catch(function(err) {
  return asyncRecovery1();
}).then(function() {
  return asyncThing4();
}, function(err) {
  return asyncRecovery2();
}).catch(function(err) {
  console.log("Don"t worry about it");
}).then(function() {
  console.log("All done!");
});
Promise API 参考

静态方法

Promise.resolve(promise);
返回一个 Promise(当且仅当 promise.constructor == Promise)

Promise.resolve(thenable);
从 thenable 对象创建一个新的 Promise。一个 thenable(类 Promise)对象是一个带有“then”方法的对象。

Promise.resolve(obj);
创建一个以 obj 为肯定结果的 Promise。

Promise.reject(obj);
创建一个以 obj 为否定结果的 Promise。为了一致性和调试便利(如堆栈追踪),obj 应该是一个 Error 实例对象。

Promise.all(array);
创建一个 Promise,当且仅当传入数组中的所有 Promise 都肯定之后才肯定,如果遇到数组中的任何一个 Promise 以否定结束,则抛出否定结果。每个数组元素都会首先经过 Promise.resolve,所以数组可以包含类 Promise 对象或者其他对象。肯定结果是一个数组,包含传入数组中每个 Promise 的肯定结果(且保持顺序);否定结果是传入数组中第一个遇到的否定结果。

Promise.race(array);
创建一个 Promise,当数组中的任意对象肯定时将其结果作为肯定结束,或者当数组中任意对象否定时将其结果作为否定结束。

Promise 的现行解决方案

其实已经有一些第三方库实现了 Promise 的功能:

Q

when

WinJS

RSVP.js

上面这些库和 JavaScript 原生 Promise 都遵守一个通用的、标准化的规范:Promises/A+,jQuery 有个类似的方法叫 Deferred,但不兼容 Promises/A+ 规范,于是会有点小问题,使用需谨慎。jQuery 还有一个 Promise 类型,它其实是 Deferred 的缩减版,所以也有同样问题。

目前的浏览器已经(部分)实现了 Promise。

Chrome 32、Opera 19 和 Firefox 29 以上的版本已经默认支持 Promise。由于是在 WebKit 内核中所以我们有理由期待下个版本的 Safari 也会支持,并且 IE 也在不断的开发中。

要在这两个浏览器上达到兼容标准 Promise,或者在其他浏览器以及 Node.js 中使用 Promise,可以看看这个 polyfill(gzip 之后 2K)。

相关参考

http://www.html5rocks.com/zh/tutorials/es6/promises/

http://www.w3ctech.com/topic/721

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise

http://www.codemud.net/~thinker/GinGin_CGI.py/show_id_doc/488

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/92321.html

相关文章

  • 2017-08-16 前端日报

    摘要:前端日报精选理解的专题之偏函数译理解事件驱动机制游戏开发前端面试中的常见的算法问题发布中文前端页面传参尚妆产品技术刊读基础系列二之实现大转盘抽奖掘金指南众成翻译编程插入排序众成翻译源码讲解函数技术风暴初体验个人文 2017-08-16 前端日报 精选 理解 JavaScript 的 async/awaitJavaScript专题之偏函数[译]理解 Node.js 事件驱动机制Pokem...

    graf 评论0 收藏0
  • react-native 体验 - 使用 javascript 来写 iOS app

    摘要:去年年初写了一个扩展十阅后,一直想写个十阅出来,奈何懒癌后期,一直拖到最近才完成原型。这次心血来潮闲的蛋疼,想起去年年初就有所耳闻的,于是就打算用它来耍耍。使用链接库在设备上运行用来断点调试地址学习资源图图图 去年年初写了一个 chrome 扩展「十阅」后,一直想写个十阅 app 出来,奈何懒癌后期,一直拖到最近才完成原型。 其实很早之前就已经写过一个 hybrid app 了,使用了...

    余学文 评论0 收藏0
  • react-native 体验 - 使用 javascript 来写 iOS app

    摘要:去年年初写了一个扩展十阅后,一直想写个十阅出来,奈何懒癌后期,一直拖到最近才完成原型。这次心血来潮闲的蛋疼,想起去年年初就有所耳闻的,于是就打算用它来耍耍。使用链接库在设备上运行用来断点调试地址学习资源图图图 去年年初写了一个 chrome 扩展「十阅」后,一直想写个十阅 app 出来,奈何懒癌后期,一直拖到最近才完成原型。 其实很早之前就已经写过一个 hybrid app 了,使用了...

    DandJ 评论0 收藏0
  • html体验#2

    摘要:但在后端代码中容易识别成注释,慎用忘记加分号啦执行为协议,这里意思为不执行任何命令忘记加分号啦执行为协议,这里意思为不执行任何命令标签也用于跳转页面,但必须有按钮或者点击才能跳转完整样式网址同标签提交必须写属性才能被提交。 碎碎念 关于布局 css布局:横向、纵向 2019年新进展:css grid git bash 上安装 http server 目的在于不使用 file:/...

    Cobub 评论0 收藏0
  • html体验#2

    摘要:但在后端代码中容易识别成注释,慎用忘记加分号啦执行为协议,这里意思为不执行任何命令忘记加分号啦执行为协议,这里意思为不执行任何命令标签也用于跳转页面,但必须有按钮或者点击才能跳转完整样式网址同标签提交必须写属性才能被提交。 碎碎念 关于布局 css布局:横向、纵向 2019年新进展:css grid git bash 上安装 http server 目的在于不使用 file:/...

    xiangzhihong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<