Promise是抽象异步处理对象以及对其进行各种操作的组件。Promise把类似的异步处理对象和处理规则进行规范化, 并按照采用统一的接口来编写,而采取规定方法之外的写法都会出错。下面是使用了Promise进行异步处理的一个例子
var promise = getAsyncPromise("fileA.txt"); promise.then(function(result){ // 获取文件内容成功时的处理}).catch(function(error){ // 获取文件内容失败时的处理});
我们可以向这个预设了抽象化异步处理的promise对象, 注册这个promise对象执行成功时和失败时相应的回调函数。除promise对象规定的方法(这里的 then 或 catch)以外的方法都是不可以使用的,所以,promise的功能是可以将复杂的异步处理轻松地进行模式化。像 Promise 这样的全局对象还拥有一些静态方法。包括 Promise.all() 还有 Promise.resolve() 等在内
简介:
Promise类似于 XMLHttpRequest,从构造函数 Promise 来创建一个新建新promise对象作为接口。
var promise = new Promise(function(resolve, reject) { // 异步处理 // 处理结束后、调用resolve 或 reject});
对通过new生成的promise对象为了设置其值在 resolve(成功) / reject(失败)时调用的回调函数 可以使用promise.then() 实例方法。
promise.then(onFulfilled, onRejected)
resolve(成功)时onFulfilled 会被调用reject(失败)时onRejected 会被调用onFulfilled、onRejected 两个都为可选参数。
异常的处理
promise.catch(onRejected)promise-workflow.js
function asyncFunction() { return new Promise(function (resolve, reject) { setTimeout(function () { resolve('Async Hello world'); }, 16); });}asyncFunction().then(function (value) { console.log(value); // => 'Async Hello world'}).catch(function (error) { console.log(error);});
new Promise构造器之后,会返回一个promise对象asyncFunction为promise对象用设置 .then 调用返回值时的回调函数。当然,像promise.then(onFulfilled, onRejected) 的方法声明一样, 如果不使用catch 方法只使用 then方法的话,如下所示的代码也能完成相同的工作。
asyncFunction().then(function (value) { console.log(value);}, function (error) { console.log(error);});
我们的任务是用Promise来通过异步处理方式来获取XMLHttpRequest(XHR)的数据。xhr-promise.js
function getURL(URL) { return new Promise(function (resolve, reject) { var req = new XMLHttpRequest(); req.open('GET', URL, true); req.onload = function () { if (req.status === 200) { resolve(req.responseText); } else { reject(new Error(req.statusText)); } }; req.onerror = function () { reject(new Error(req.statusText)); }; req.send(); });}// 运行示例var URL = "http://httpbin.org/get";getURL(URL).then(function onFulfilled(value){ console.log(value);}).catch(function onRejected(error){ console.error(error);});
异步操作
var promise = new Promise(function (resolve){ console.log("inner promise"); // 1 resolve(42);});promise.then(function(value){ console.log(value); // 3});console.log("outer promise"); // 2
输出顺序为1,2,3同步调用和异步调用同时存在导致的混乱mixed-onready.js会根据执行时DOM是否已经装载完毕来决定是对回调函数进行同步调用还是异步调用。
function onReady(fn) { var readyState = document.readyState; if (readyState === 'interactive' || readyState === 'complete') { fn(); } else { window.addEventListener('DOMContentLoaded', fn); }}onReady(function () { console.log('DOM fully loaded and parsed');});console.log('==Starting==');
如果在调用onReady之前DOM已经载入的话对回调函数进行同步调用如果在调用onReady之前DOM还没有载入的话通过注册 DOMContentLoaded 事件监听器来对回调函数进行异步调用因此,如果这段代码在源文件中出现的位置不同,在控制台上打印的log消息顺序也会不同。为了解决这个问题,我们可以选择统一使用异步调用的方式。
function onReady(fn) { var readyState = document.readyState; if (readyState === 'interactive' || readyState === 'complete') { setTimeout(fn, 0); } else { window.addEventListener('DOMContentLoaded', fn); }}onReady(function () { console.log('DOM fully loaded and parsed');});console.log('==Starting==');
前面我们看到的 promise.then 也属于此类,为了避免上述中同时使用同步、异步调用可能引起的混乱问题,Promise在规范上规定 Promise只能使用异步调用方式 。
function onReadyPromise() { return new Promise(function (resolve, reject) { var readyState = document.readyState; if (readyState === 'interactive' || readyState === 'complete') { resolve(); } else { window.addEventListener('DOMContentLoaded', resolve); } });}onReadyPromise().then(function () { console.log('DOM fully loaded and parsed');});console.log('==Starting==');
由于Promise保证了每次调用都是以异步方式进行的,所以我们在实际编码中不需要调用 setTimeout 来自己实现异步调用。
promise chain(方法链)
function taskA() { console.log("Task A"); throw new Error("throw Error @ Task A")}function taskB() { console.log("Task B");// 不会被调用}function onRejected(error) { console.log(error);// => "throw Error @ Task A"}function finalTask() { console.log("Final Task");}var promise = Promise.resolve();promise .then(taskA) .then(taskB) .catch(onRejected) .then(finalTask);
执行这段代码我们会发现 Task B 是不会被调用的。promise chain 中如何传递参数这时候如果 Task A 想给 Task B 传递一个参数该怎么办呢?答案非常简单,那就是在 Task A 中 return 的返回值,会在 Task B 执行时传给它。
function doubleUp(value) { return value * 2;}function increment(value) { return value + 1;}function output(value) { console.log(value);// => (1 + 1) * 2}var promise = Promise.resolve(1);promise .then(increment) .then(doubleUp) .then(output) .catch(function(error){ // promise chain中出现异常的时候会被调用 console.error(error); });
Promise#catch
IE 8浏览器实用catch(ECMAScript3保留字)作为属性会出现 identifier not found,ECMAScript5保留字可以作为属性
var promise = Promise.reject(new Error("message"));promise.catch(function (error) { console.error(error);});
可以变换两种下面两种写法1.用 catch标识符解决冲突问题
var promise = Promise.reject(new Error("message"));promise["catch"](function (error) { console.error(error);});
2.改用then代替
var promise = Promise.reject(new Error("message"));promise.then(undefined, function (error) { console.error(error);});
正确的 返回返回新创建的promise对象
function anAsyncCall() { var promise = Promise.resolve(); return promise.then(function() { // 任意处理 return newVar; });}
使用Promise#then同时处理多个异步请求
function getURL(URL) { return new Promise(function (resolve, reject) { var req = new XMLHttpRequest(); req.open('GET', URL, true); req.onload = function () { if (req.status === 200) { resolve(req.responseText); } else { reject(new Error(req.statusText)); } }; req.onerror = function () { reject(new Error(req.statusText)); }; req.send(); });}var request = { comment: function getComment() { return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse); }, people: function getPeople() { return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse); } };function main() { function recordValue(results, value) { results.push(value); return results; } // [] 用来保存初始化的值 var pushValue = recordValue.bind(null, []); return request.comment().then(pushValue).then(request.people).then(pushValue);}// 运行的例子main().then(function (value) { console.log(value);}).catch(function(error){ console.error(error);});
为了应对这种需要对多个异步调用进行统一处理的场景,Promise准备了 Promise.all 和 Promise.race 这两个静态方法。
Promise.all
Promise.all 接收一个 promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用 .then 方法。
function getURL(URL) { return new Promise(function (resolve, reject) { var req = new XMLHttpRequest(); req.open('GET', URL, true); req.onload = function () { if (req.status === 200) { resolve(req.responseText); } else { reject(new Error(req.statusText)); } }; req.onerror = function () { reject(new Error(req.statusText)); }; req.send(); });}var request = { comment: function getComment() { return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse); }, people: function getPeople() { return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse); } };function main() { return Promise.all([request.comment(), request.people()]);}// 运行示例main().then(function (value) { console.log(value);}).catch(function(error){ console.log(error);});
这个例子的执行方法和 前面的例子 一样。 不过Promise.all 在以下几点和之前的例子有所不同。main中的处理流程显得非常清晰Promise.all 接收 promise对象组成的数组作为参数
Promise.race
接着我们来看看和 Promise.all 类似的对多个promise对象进行处理的 Promise.race 方法。它的使用方法和Promise.all一样,接收一个promise对象数组为参数。Promise.all 在接收到的所有的对象promise都变为 FulFilled 或者 Rejected 状态之后才会继续进行后面的处理, 与之相对的是 Promise.race 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。
// `delay`毫秒后执行resolvefunction timerPromisefy(delay) { return new Promise(function (resolve) { setTimeout(function () { resolve(delay); }, delay); });}// 任何一个promise变为resolve或reject 的话程序就停止运行Promise.race([ timerPromisefy(1), timerPromisefy(32), timerPromisefy(64), timerPromisefy(128)]).then(function (value) { console.log(value); // => 1});