ES6 Promise

问题

JavaScript的Callback机制深入人心。而ECMAScript的世界同样充斥的各种异步操作(异步IO、setTimeout等)。异步和Callback的搭载很容易就衍生"回调金字塔"。——由此产生Deferred/Promise。

Deferred起源于Python,后来被CommonJS挖掘并发扬光大,得到了大名鼎鼎的Promise,并且已经纳入ECMAScript 6(JavaScript下一版本)。

Promise/Deferred是当今最著名的异步模型,不仅强壮了JavaScript Event Loop(事件轮询)机制下异步代码的模型,同时增强了异步代码的可靠性。

有了 Promise ,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。

概述

Promise 对象用于延迟(deferred) 计算和异步(asynchronous ) 计算。一个Promise对象代表着一个还未完成,但预期将来会完成的操作。

语法

    new Promise(executor);
    new Promise(function(resolve, reject) { ... });

例子:


var promise = new Promise(function(resolve, reject) {
if (/** 异步操作成功* */){
resolve(value);
} else {
reject(error);
}
});


promise.then(function(value) {
// success
},
function(value) {
// failure
});

参数

executor带有 resolvereject 两个参数的函数对象。一旦我们的操作完成即可调用这些函数。

  • 第一个参数用在处理执行成功的场景
  • 第二个参数则用在处理执行失败的场景。

描述

Promise对象是一个返回值的代理,这个返回值在promise对象创建时未必已知。它允许你为异步操作的成功或失败指定处理方法。 这使得异步方法可以像同步方法那样返回值:异步方法会返回一个包含了原返回值的 promise 对象来替代原返回值。

Promise对象有以下几种状态:

  • pending: 初始状态, 非 fulfilled 或 rejected.
  • fulfilled: 成功的操作.
  • rejected: 失败的操作.

pending状态的promise对象既可转换为带着一个成功值的fulfilled 状态,也可变为带着一个失败信息的 rejected 状态。

特点

  • 对象的状态不受外界影响,只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
    当状态发生转换时,promise.then绑定的方法(函数句柄)就会被调用。

  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

当绑定方法时,如果 promise对象已经处于 fulfilled 或 rejected 状态,那么相应的方法将会被立刻调用, 所以在异步操作的完成情况和它的绑定方法之间不存在竞争条件。
因为Promise.prototype.then
和 Promise.prototype.catch

方法返回 promises对象, 所以它们可以被链式调用—— 一种被称为 composition 的操作。


注意: 如果一个promise对象处在fulfilled或rejected状态而不是pending状态,那么它也可以被称为
settled
状态。你可能也会听到一个术语 resolved ,它表示promise对象处于settled状态,或者promise对象被锁定在了调用链中。关于promise的状态, Domenic Denicola 的 States and fates 有更多详情可供参考。

基本的api

  • Promise.resolve()
  • Promise.reject()
  • Promise.prototype.then()
  • Promise.prototype.catch()
  • Promise.all() 完成全部
  • Promise.race() 完成一个

常用的JavaScript的promise的写法


function get(uri){
return http(uri, 'GET', null);
}


function post(uri,data){
if(typeof data === 'object' && !(data instanceof String || (FormData && data instanceof FormData))) {
var params = [];
for(var p in data) {
if(data[p] instanceof Array) {
for(var i = 0; i < data[p].length; i++) {
params.push(encodeURIComponent(p) + '[]=' + encodeURIComponent(data[p][i]));
}
} else {
params.push(encodeURIComponent(p) + '=' + encodeURIComponent(data[p]));
}
}
data = params.join('&');
}

return http(uri, 'POST', data || null, {
    "Content-type":"application/x-www-form-urlencoded"
});

}

function http(uri,method,data,headers){
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method,uri,true);
if(headers) {
for(var p in headers) {
xhr.setRequestHeader(p, headers[p]);
}
}
xhr.addEventListener('readystatechange',function(e){
if(xhr.readyState === 4) {
if(String(xhr.status).match(/^2\d\d$/)) {
resolve(xhr.responseText);
} else {
reject(xhr);
}
}
});
xhr.send(data);
})
}


function wait(duration){
return new Promise(function(resolve, reject) {
setTimeout(resolve,duration);
})
}


function waitFor(element,event,useCapture){
return new Promise(function(resolve, reject) {
element.addEventListener(event,function listener(event){
resolve(event)
this.removeEventListener(event, listener, useCapture);
},useCapture)
})
}


function loadImage(src) {
return new Promise(function(resolve, reject) {
var image = new Image;
image.addEventListener('load',function listener() {
resolve(image);
this.removeEventListener('load', listener, useCapture);
});
image.src = src;
image.addEventListener('error',reject);
})
}


function runScript(src) {
return new Promise(function(resolve, reject) {
var script = document.createElement('script');
script.src = src;
script.addEventListener('load',resolve);
script.addEventListener('error',reject);
(document.getElementsByTagName('head')[0] || document.body || document.documentElement).appendChild(script);
})
}


function domReady() {
return new Promise(function(resolve, reject) {
if(document.readyState === 'complete') {
resolve();
} else {
document.addEventListener('DOMContentLoaded',resolve);
}
})
}

兼容性垫片polyfill of the ES6 Promise

es6-promise
es6-promise

参考资料:

  • promisejs
  • MDN-javascript
  • MDN-Promise
  • 阮一峰ES6-promise
  • ECMAScript 6 的正式版
  • JavaScript Promise迷你书(中文版)
  • Promise/A+规范
  • 剖析 Promise 之基础篇

你可能感兴趣的:(ES6 Promise)