[ES6] promise
Promise
new Promise(executor);
new Promise(function(resolve, reject) { ... });
Promise 对象用于延迟(deferred) 计算和异步(asynchronous ) 计算。
一个Promise对象代表着一个还未完成,但预期将来会完成的操作。
Promise 对象是一个返回值的代理,这个返回值在promise对象创建时未必已知。
它允许你为异步操作的成功或失败指定处理方法。
这使得异步方法可以像同步方法那样返回值:异步方法会返回一个包含了原返回值的 promise 对象来替代原返回值。
Promise对象有以下几种状态:
pending: 初始状态, 非 fulfilled 或 rejected.
fulfilled: 成功的操作.
rejected: 失败的操作.
pending状态的promise对象既可转换为带着一个成功值的fulfilled 状态,也可变为带着一个失败信息的 rejected 状态。
当状态发生转换时,promise.then绑定的方法(函数句柄)就会被调用。
(当绑定方法时,如果 promise对象已经处于 fulfilled 或 rejected 状态,那么相应的方法将会被立刻调用, 所以在异步操作的完成情况和它的绑定方法之间不存在竞争条件。)
因为Promise.prototype.then和 Promise.prototype.catch方法返回 promises对象,
所以它们可以被链式调用—— 一种被称为 composition 的操作。
如果一个promise对象处在fulfilled或rejected状态而不是pending状态,那么它也可以被称为settled状态。
你可能也会听到一个术语resolved ,它表示promise对象处于settled状态,或者promise对象被锁定在了调用链中。
#
var promise = new Promise(function(resolve, reject) { // ... some code if (/* 异步操作成功 */){ resolve(value); } else { reject(error); } });
#
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); });
#
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <style> </style> </head> <body> <div id="log"></div> <button onclick="testPromise()">click</button> <script> 'use strict'; var promiseCount = 0; function testPromise() { var thisPromiseCount = ++promiseCount; var log = document.getElementById('log'); log.insertAdjacentHTML('beforeend', thisPromiseCount + ') Started (<small>Sync code started</small>)<br/>'); // We make a new promise: we promise a numeric count of this promise, starting from 1 (after waiting 3s) var p1 = new Promise( // The resolver function is called with the ability to resolve or // reject the promise function(resolve, reject) { log.insertAdjacentHTML('beforeend', thisPromiseCount + ') Promise started (<small>Async code started</small>)<br/>'); // This is only an example to create asynchronism window.setTimeout(function() { // We fulfill the promise ! resolve(thisPromiseCount); }, Math.random() * 2000 + 1000); } ); // We define what to do when the promise is resolved/fulfilled with the then() call, // and the catch() method defines what to do if the promise is rejected. p1.then( // Log the fulfillment value function(val) { log.insertAdjacentHTML('beforeend', val + ') Promise fulfilled (<small>Async code terminated</small>)<br/>'); }).catch( // Log the rejection reason function(reason) { console.log('Handle rejected promise (' + reason + ') here.'); }); log.insertAdjacentHTML('beforeend', thisPromiseCount + ') Promise made (<small>Sync code terminated</small>)<br/>'); } </script> </body> </html>
#
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <style> </style> </head> <body> <div id="log"></div> <button onclick="testPromise()">click</button> <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/>'); } </script> </body> </html>
#XMLHttpRequest
'use strict'; // A-> $http function is implemented in order to follow the standard Adapter pattern function $http(url) { // A small example of object var core = { // Method that performs the ajax request ajax: function(method, url, args) { // Creating a promise var promise = new Promise(function(resolve, reject) { // Instantiates the XMLHttpRequest var client = new XMLHttpRequest(); var uri = url; if (args && (method === 'POST' || method === 'PUT')) { uri += '?'; var argcount = 0; for (var key in args) { if (args.hasOwnProperty(key)) { if (argcount++) { uri += '&'; } uri += encodeURIComponent(key) + '=' + encodeURIComponent(args[key]); } } } client.open(method, uri); client.send(); client.onload = function() { if (this.status >= 200 && this.status < 300) { // Performs the function "resolve" when this.status is equal to 2xx resolve(this.response); } else { // Performs the function "reject" when this.status is different than 2xx reject(this.statusText); } }; client.onerror = function() { reject(this.statusText); }; }); // Return the promise return promise; } }; // Adapter pattern return { 'get': function(args) { return core.ajax('GET', url, args); }, 'post': function(args) { return core.ajax('POST', url, args); }, 'put': function(args) { return core.ajax('PUT', url, args); }, 'delete': function(args) { return core.ajax('DELETE', url, args); } }; }; // End A // B-> Here you define its functions and its payload var mdnAPI = 'xx/search.json'; var payload = { 'topic': 'js', 'q': 'Promise' }; var callback = { success: function(data) { console.log(1, 'success', JSON.parse(data)); }, error: function(data) { console.log(2, 'error', JSON.parse(data)); } }; // End B // Executes the method call $http(mdnAPI) .get(payload) .then(callback.success) .catch(callback.error); // Executes the method call but an alternative way (1) to handle Promise Reject case $http(mdnAPI) .get(payload) .then(callback.success, callback.error); // Executes the method call but an alternative way (2) to handle Promise Reject case $http(mdnAPI) .get(payload) .then(callback.success) .then(undefined, callback.error);
#imgload
function imgLoad(url) { // Create new promise with the Promise() constructor; // This has as its argument a function // with two parameters, resolve and reject return new Promise(function(resolve, reject) { // Standard XHR to load an image var request = new XMLHttpRequest(); request.open('GET', url); request.responseType = 'blob'; // When the request loads, check whether it was successful request.onload = function() { if (request.status === 200) { // If successful, resolve the promise by passing back the request response resolve(request.response); } else { // If it fails, reject the promise with a error message reject(Error('Image didn\'t load successfully; error code:' + request.statusText)); } }; request.onerror = function() { // Also deal with the case when the entire request fails to begin with // This is probably a network error, so reject the promise with an appropriate message reject(Error('There was a network error.')); }; // Send the request request.send(); }); } // Get a reference to the body element, and create a new image object var body = document.querySelector('body'); var myImage = new Image(); // Call the function with the URL we want to load, but then chain the // promise then() method on to the end of it. This contains two callbacks imgLoad('myLittleVader.jpg').then(function(response) { // The first runs when the promise resolves, with the request.reponse // specified within the resolve() method. var imageURL = window.URL.createObjectURL(response); myImage.src = imageURL; body.appendChild(myImage); // The second runs when the promise // is rejected, and logs the Error specified with the reject() method. }, function(Error) { console.log(Error); });
Promise.prototype.then()
p.then(onFulfilled, onRejected);
p.then(function(value) {
// fulfillment 满足
}, function(reason) {
// rejection 拒绝
});
then()方法返回一个Promise。
它有两个参数,分别为Promise在 success 和 failure 情况下的回调函数。
由于 then 和 Promise.prototype.catch() 方法返回promise, 它们可以被链式调用 — 一种称为 composition 的操作.
#使用then方法
var p1 = new Promise(function (resolve, reject) {
resolve("成功!");
// or
reject ("错误!");
});
p1.then(function (value) {
console.log(value); // 成功!
}, function (reason) {
console.log(reason); // 错误!
});
#链式调用
因为then方法返回一个Promise,可以轻易地链式调用then。
var p2 = new Promise(function (resolve, reject) { resolve(1); }); p2.then(function (value) { console.log(value); // 1 return value + 1; }).then(function (value) { console.log(value); // 2 }); p2.then(function (value) { console.log(value); // 1 return value; })
#也可以在别的函数上用Promise的基础API实现链式操作。?
function fetch_current_data() { // fetch() API 返回一个Promise对象。 // 这个函数暴露了一个类似的API, 除了返回成功值 // 你还可以做点别的。 return fetch("current-data.json").then((response) => { if (response.headers.get("content-type") != "application/json") { throw new TypeError(); } var j = response.json(); // 可以针对j的做其他操作 return j; // 返回给fetch_current_data().then()调用者的值 }); }
Promise.prototype.catch()
p.catch(onRejected);
p.catch(function(reason) {
// rejection 拒绝
});
catch() 方法只处理Promise被拒绝的情况,并返回一个Promise。
该方法的行为和调用Promise.prototype.then(undefined, onRejected)相同。
#使用catch方法
var p1 = new Promise(function (resolve, reject) { resolve("成功"); }); p1.then(function (value) { console.log(value); // "成功!" throw "哦,不!"; }).catch(function (e) { console.log(e); // "哦,不!" });
#
var p1 = new Promise(function (resolve, reject) { resolve('Success'); }); p1.then(function (value) { console.log(value); // "Success!" throw 'oh, no!'; }).catch(function (e) { console.log(e); // "oh, no!" }).then(function () { console.log('after a catch the chain is restored');//'after a catch the chain is restored' }, function () { console.log('Not fired due to the catch'); }); // The following behaves the same as above p1.then(function (value) { console.log(value); // "Success!" return Promise.reject('oh, no!'); }).catch(function (e) { console.log(e); // "oh, no!" }).then(function () { console.log('after a catch the chain is restored');//'after a catch the chain is restored' }, function () { console.log('Not fired due to the catch'); });
Promise.resolve()
Promise.resolve(value);
Promise.resolve(promise);
Promise.resolve(thenable);
Promise.resolve(value)方法返回一个以给定值解析后的Promise对象。
但如果这个值是个thenable(即带有then方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态(指resolved/rejected/pending/settled);
否则以该值为成功状态返回promise对象。
#使用静态方法Promise.resolve
Promise.resolve("Success").then(function (value) { console.log(value); // "Success" }, function (value) { // 不会被调用 });
#对一个数组进行resolve
var p = Promise.resolve([1, 2, 3]); p.then(function (v) { console.log(v[0]); // 1 });
#Resolve另一个promise对象
var original = Promise.resolve(true); var cast = Promise.resolve(original); cast.then(function (v) { console.log(v); // true });
#resolve thenable的对象们并抛出错误
// Resolve一个thennable对象 var p1 = Promise.resolve({ then: function (onFulfill, onReject) { onFulfill("fulfilled!"); } }); console.log(p1 instanceof Promise) // true 这是一个Promise对象 p1.then(function (v) { console.log(v); // 输出"fulfilled!" }, function (e) { // 不会被调用 }); // Thenable在callback之前抛出异常 // Promise rejects var thenable = { then: function (resolve) { throw new TypeError("Throwing"); resolve("Resolving"); } }; var p2 = Promise.resolve(thenable); p2.then(function (v) { // 不会被调用 }, function (e) { console.log(e); // TypeError: Throwing }); // Thenable在callback之后抛出异常 // Promise resolves var thenable = { then: function (resolve) { resolve("Resolving"); throw new TypeError("Throwing"); } }; var p3 = Promise.resolve(thenable); p3.then(function (v) { console.log(v); // 输出"Resolving" }, function (e) { // 不会被调用 });
Promise.race()
Promise.race(iterable);
Promise.race(iterable)方法返回一个promise,
这个promise在iterable中的任意一个promise被解决或拒绝后,
立刻以相同的解决值被解决或以相同的拒绝原因被拒绝。
#race 函数返回一个Promise,这个Promise根据传入的Promise中的第一个确定状态--不管是接受还是拒绝--的状态而确定状态。
var p1 = new Promise(function (resolve, reject) { setTimeout(resolve, 500, "一"); }); var p2 = new Promise(function (resolve, reject) { setTimeout(resolve, 100, "二"); }); Promise.race([p1, p2]).then(function (value) { console.log(value); // "二" // 两个都解决,但p2更快 }); var p3 = new Promise(function (resolve, reject) { setTimeout(resolve, 100, "三"); }); var p4 = new Promise(function (resolve, reject) { setTimeout(reject, 500, "四"); }); Promise.race([p3, p4]).then(function (value) { console.log(value); // "三" // p3更快,所以被解决(resolve)了 }, function (reason) { // 未被执行 }); var p5 = new Promise(function (resolve, reject) { setTimeout(resolve, 500, "五"); }); var p6 = new Promise(function (resolve, reject) { setTimeout(reject, 100, "六"); }); Promise.race([p5, p6]).then(function (value) { // 未被执行 }, function (reason) { console.log(reason); // "六" // p6更快,所以被拒绝(reject了) });
Promise.reject()
Promise.reject(reason);
Promise.reject(reason)方法返回一个用reason拒绝的Promise。
#使用静态Promise.reject方法
Promise.reject("Testing static reject").then(function (reason) { // 未被调用 }, function (reason) { console.log(reason); // "Testing static reject" 测试静态拒绝 }); Promise.reject(new Error("fail")).then(function (error) { // 未被调用 }, function (error) { console.log(error); // Error: fail(…) 堆栈跟踪 });
Promise.all()
Promise.all(iterable);
all里的方法全部一起执行,并没有顺序之分,一个出错后续不再resolve。
Promise.all(iterable) 方法返回一个promise,该promise会在iterable参数内的所有promise都被解决后被解决,
或者 reject 第一个进入拒绝状态的 promise 的拒绝原因。
结果是promise的一组值。
如果传入的可迭代数组中某项不是一个promise,该项会被用Promise.resolve转换为一个promise。
如果任一传入的promise被拒绝了,all Promise立刻带着该promise的拒绝原因进入拒绝(rejected)状态,不再理会其它传入的promise是否被解决。
#
var promise = Promise.resolve(3); Promise.all([true, promise]) .then(values => { console.log(values); // [true, 3] });
#Promise.all waits for all fulfillments (or the first rejection).
var p1 = Promise.resolve(3); var p2 = 1337; var p3 = new Promise(function (resolve, reject) { setTimeout(resolve, 100, "foo"); }); Promise.all([p1, p2, p3]).then(function (values) { console.log(values); // [3, 1337, "foo"] });
#Promise.all fail-fast behaviour
只有有一个reject ,则立马全部进入reject
var p1 = new Promise(function (resolve, reject) { setTimeout(resolve, 1000, "one"); }); var p2 = new Promise(function (resolve, reject) { setTimeout(resolve, 2000, "two"); }); var p3 = new Promise(function (resolve, reject) { setTimeout(resolve, 3000, "three"); }); var p4 = new Promise(function (resolve, reject) { setTimeout(resolve, 4000, "four"); }); var p5 = new Promise(function (resolve, reject) { reject("reject"); }); Promise.all([p1, p2, p3, p4, p5]).then(function (value) { console.log(value); }, function (reason) { console.log(reason)//"reject" });