Promise
描述:
Promise,就是一个对象,简单说就是一个容器,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API,可供进一步处理。
promise代表一个异步操作的执行返回状态,这个执行返回状态在promise对象创建时未必已知。它允许你为异步操作的成功或失败指定处理方法。
这使得异步方法可以像同步方法那样返回值:异步方法会返回一个包含了原返回状态的 promise 对象来替代原返回状态。
特点:
Promise 对象有以下两个特点。
(1)对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:
Pending(进行中)、
Resolved(已完成,又称 Fulfilled)
Rejected(已失败)。
只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
pending状态的promise对象既可转换为带着一个成功值的fulfilled 状态,也可变为带着一个失败信息的 rejected 状态。当状态发生转换时,promise.then绑定的方法(函数句柄)就会被调用。(当绑定方法时,如果 promise对象已经处于 fulfilled 或 rejected 状态,那么相应的方法将会被立刻调用, 所以在异步操作的完成情况和它的绑定方法之间不存在竞争条件。)
因为Promise.prototype.then
和 Promise.prototype.catch
方法返回 promises对象, 所以它们可以被链式调用—— 一种被称为 composition 的操作。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,就不会再变了。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。
Promise的适用场景:
Promise可以用来避免异步操作函数里的嵌套回调(callback hell)问题,因为解决异步最直接的方法是回调嵌套,将后一个的操作放在前一个操作的异步回调里,但如果操作多了,就会有很多层的嵌套。
Promise并非适用于所有的异步场景,例如事件的绑定,某个程度上Promise有点类似事件的监听回调,当触发某个操作时进行后面特定的逻辑。但Promise只能执行一次,且需要前面特定的操作执行完成才会进行下一步,一般分成功和失败两种场景,成功或失败后会立即执行响应函数。
promise很适合判断一个比较耗时的操作是否最终执行成功的场景,如我们通常理解的ajax网络请求、读取localstorage等操作。
不同的Promise差异基本表现如下:
-
构造Promise对象 new Promise().resolve() 或者newPomise(function(resolve, reject) {})
-
是否有 .done().fail().always() 等方法
-
是否有Promise.all()方法
-
是否有isRejected()isResolved()
-
.then() return 结果链式的
基本语法:
属性
Promise.length
长度属性,其值为 1 (构造器参数的数目).
Promise.prototype
表示 Promise
构造器的原型
方法
Promise.all(iterable)
返回一个promise对象,当iterable参数里所有的promise都被解决后,该promise也会被解决。
Promise.race(iterable)
当iterable参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。
Promise.reject(reason)
调用Promise的rejected句柄,并返回这个Promise对象。
Promise.resolve(value)
用成功值value解决一个Promise对象。如果该value为可继续的(thenable,即带有then方法),返回的Promise对象会“跟随”这个value,采用这个value的最终状态;否则的话返回值会用这个value满足(fullfil)返回的Promise对象。
Promise
原型
属性
Promise.prototype.constructor
返回创建了实例原型的函数. 默认为 Promise
函数.
方法
Promise.prototype.catch(onRejected)
添加一个否定(rejection) 回调到当前 promise, 返回一个新的promise。如果这个回调被调用,新 promise 将以它的返回值来resolve,否则如果当前promise 进入fulfilled状态,则以当前promise的肯定结果作为新promise的肯定结果.
Promise.prototype.then(onFulfilled, onRejected)
添加肯定和否定回调到当前 promise, 返回一个新的 promise, 将以回调的返回值 来resolve.
示例:
ES6规定,Promise对象是一个构造函数,用来生成Promise实例。
下面代码创造了一个Promise实例。
var promise = new Promise(function(resolve, reject) { // ... some code if (/* 异步操作成功 */){ resolve(value); } else { reject(error); } });
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
。它们是两个函数,由JavaScript引擎提供,不用自己部署。
resolve
函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从Pending变为Resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject
函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从Pending变为Rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数。
promise.then(function(value) { // success }, function(value) { // failure });
then
方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为Resolved时调用,第二个回调函数是Promise对象的状态变为Reject时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。
下面是一个Promise对象的简单例子。
function timeout(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms, 'done'); }); } timeout(100).then((value) => { console.log(value); });
上面代码中,timeout
方法返回一个Promise实例,表示一段时间以后才会发生的结果。过了指定的时间(ms
参数)以后,Promise实例的状态变为Resolved,就会触发then
方法绑定的回调函数。
Promise新建后就会立即执行。
let promise = new Promise(function(resolve, reject) { console.log('Promise'); resolve(); }); promise.then(function() { console.log('Resolved.'); }); console.log('Hi!'); // Promise // Hi! // Resolved
上面代码中,Promise新建后立即执行,所以首先输出的是“Promise”。然后,then
方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以“Resolved”最后输出。
异步加载图片
function loadImageAsync(url) { return new Promise(function(resolve, reject) { var image = new Image(); image.onload = function() { resolve(image); }; image.onerror = function() { reject(new Error('Could not load image at ' + url)); }; image.src = url; }); }
下面是一个用Promise对象实现的Ajax操作的例子。
var getJSON = function(url) { var promise = new Promise(function(resolve, reject){ var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.responseType = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler() { if ( this.readyState !== 4 ) { return; } if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } }; }); return promise; }; getJSON("/posts.json").then(function(json) { console.log('Contents: ' + json); }, function(error) { console.error('出错了', error); });
上面代码中,getJSON
是对XMLHttpRequest对象的封装,用于发出一个针对JSON数据的HTTP请求,并且返回一个Promise对象。需要注意的是,在getJSON
内部,resolve
函数和reject
函数调用时,都带有参数。
如果调用resolve
函数和reject
函数时带有参数,那么它们的参数会被传递给回调函数。reject
函数的参数通常是Error对象的实例,表示抛出的错误;resolve
函数的参数除了正常的值以外,还可能是另一个Promise实例,表示异步操作的结果有可能是一个值,也有可能是另一个异步操作,比如像下面这样。
var p1 = new Promise(function(resolve, reject){ // ... }); var p2 = new Promise(function(resolve, reject){ // ... resolve(p1); })
上面代码中,p1
和p2
都是Promise的实例,但是p2
的resolve
方法将p1
作为参数,即一个异步操作的结果是返回另一个异步操作。
注意,这时p1
的状态就会传递给p2
,也就是说,p1
的状态决定了p2
的状态。如果p1
的状态是Pending
,那么p2
的回调函数就会等待p1
的状态改变;如果p1
的状态已经是Resolved
或者Rejected
,那么p2
的回调函数将会立刻执行。
var p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('fail')), 3000) }) var p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000) }) p2.then(result => console.log(result)) p2.catch(error => console.log(error)) // Error: fail
上面代码中,p1
是一个Promise,3秒之后变为rejected
。p2
的状态由p1
决定,1秒之后,p2
调用resolve
方法,但是此时p1
的状态还没有改变,因此p2
的状态也不会变。又过了2秒,p1
变为rejected
,p2
也跟着变为rejected
。
下面这个例子展示了Promise的机制。每当
<button>
被按下时,testPromise()
函数就会被执行。该函数会创建一个用window.setTimeout在1到3秒(随机)后用‘result’字符串解决的promise。
这里通过p1.then方法
的满足回调,简单的输出了promise的满足过程,这些输出显示了该方法的同步部分是如何和promise的异步解决解耦的
这个例子在按钮被按下后执行。你需要一个支持Promise的浏览器。你能通过短时间内按多次按钮看到不同的promise一个接一个的被满足。
<div id="log"></div> <script> 'use strict'; var promiseCount = 0; function testPromise() { var thisPromiseCount = ++promiseCount; var log = document.getElementById('log'); log.insertAdjacentHTML('beforeend', thisPromiseCount + ') 开始(同步代码开始)<br/>'); // 我们创建一个新的promise: 然后用'result'字符串解决这个promise (3秒后) var p1 = new Promise(function (resolve, reject) { // 解决函数带着解决(resolve)或拒绝(reject)promise的能力被执行 log.insertAdjacentHTML('beforeend', thisPromiseCount + ') Promise开始(异步代码开始)<br/>'); // 这只是个创建异步解决的示例 window.setTimeout(function () { // 我们满足(fullfil)了这个promise! resolve(thisPromiseCount) }, Math.random() * 2000 + 1000); }); // 定义当promise被满足时应做什么 p1.then(function (val) { // 输出一段信息和一个值 log.insertAdjacentHTML('beforeend', val + ') Promise被满足了(异步代码结束)<br/>'); }); log.insertAdjacentHTML('beforeend', thisPromiseCount + ') 建立了Promise(同步代码结束)<br/><br/>'); } testPromise(); testPromise(); testPromise(); </script>
总结:
实现异步的方法目前有,自定义嵌套,Promise、generator、Defferd,还有ES7的async(generator的封装),不同场景可以选择不同的方式去实现
参考链接:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
http://ouvens.github.io/frontend-javascript/2016/03/04/promise-and-asyncjs.html
http://es6.ruanyifeng.com/#docs/promise
http://bluebirdjs.com/docs/api-reference.html
https://github.com/ouvens/promise 简单的Promise实现样例
https://github.com/tj/co generator异步实现