Promise 对象用来传递异步操做消息,表明一个将来才会知道结果的事件,而且对不一样事件提供统一的 API 以便进一步处理。Promise 具备如下特色:ajax
对象状态不受外界影响:Promise 表明的异步操做有三个状态:json
一旦状态改变,就不会再变:Promise 的状态只有2种可能:数组
对于同一个 promise, 当以上状态发生一个(只能发生其一),就不会再改变了。以后任什么时候间你都能获得这个状态,且永不改变。
有了 Promise 就能够将层层的回调写为同步的样子,表示起来更清晰。不过须要注意如下几点:promise
Promise 的基本结构以下:浏览器
var promise = new Promise(function(resolve, reject){ if(/*异步操做成功*/){ resolve(value); } else { reject(error); } });
构造函数接受一个回调函数为参数,回调函数具备2个参数,也都是函数,resolve 在 Promise 状态变为 resolved 时调用,reject 在 Promise 状态变为 rejected 时调用。resolve 的接受一个参数——值或另外一个 promise 对象; rejectj接受一个参数——错误。须要说明的是,这里的 resole 和 reject 函数已经由系统部署好了,咱们能够不写。服务器
promise 构建好之后咱们就能够调用它的then()
方法,then(resolve(value){},reject(value){})
方法接受2个函数参数,resolve 在 Promise 状态变为 resolved 时调用,reject 在 Promise 状态变为 rejected 时调用。其中 reject 参数是可选的。和构造函数不一样的是,then 方法的 reject 和 resolve 都使用 promise 传出的值做为其惟一的参数。app
这里写一个简单的例子,理解一下:异步
function timeout(ms){ return new Promise((resolve, reject) => { console.log("promise"); //"promise" setTimeout(resolve, ms, 'done'); }); } timeout(2000).then((value) => { console.log(value); //2秒后获得 "done" });
利用 Promise 异步加载图片:async
function loadImageAsync(url){ return new Promise(function(resole, 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 id = document.getElementById("primary"); var getJSON = function(url){ var promise = new Promise(function(resolve, reject){ var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.response = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler(){ if(client.readyState !== 4) return; if(this.status === 200){ resolve(client.response); } else { reject(new Error(this.statusText)); } } }); return promise; } getJSON('info.json').then( json => id.innerHTML = "<pre>" + json + "</pre>", err => id.innerHTML = err );
若是 resolve 的参数是一个promise:
var p1 = new Promise(function(resolve, reject){ //... }); var p2 = new Promise(function(resolve, reject){ //... resolve(p1); });
上面代码中 p1 的状态传给了 p2,也就是p1运行完成(状态为 resolve 或 reject)后 p2 的回调函数会马上开始执行:
var p1 = new Promise(function(resolve, reject){ setTimeout(() => reject(new Error('failed')), 3000); }); var p2 = new Promise(function(resolve, reject){ setTimeout(() => resolve(p1), 1000); }); p2.then(result => console.log(result)); p2.catch(error => console.log(error));
p1 创建,进入 setTimeout 异步计时器。以后 p2 创建,进入 setTimeout 异步计时器。1s 后 p2 准备执行 resolve, 可是 resolve 的参数是 p1, 此时 p1 仍是 Pending 状态,因此 p2 开始等待。又过了 2s, p1 的 reject 执行,变为 rejected 状态,随即 p2 也跟着变成 rejected 状态。
then(resolve(value){},reject(value){})
方法接受2个函数参数,resolve 在 Promise 状态变为 resolved 时调用,reject 在 Promise 状态变为 rejected 时调用。其中 reject 参数是可选的。和构造函数不一样的是,then 方法的 reject 和 resolve 都使用 promise 传出的值做为其惟一的参数。then()
方法返回一个新的 Promise 实例,注意,不是以前那个。所以能够用链式调用,不断添加"回调"函数。 then 的返回值成了下一个 then 中回调函数的参数:
var p = new Promise(function(resolve, reject){ resolve("from new Promise"); }).then(function (value){ console.log(value); //from new Promise 其次输出这个 return "from the first 'then'"; }).then(function(value){ console.log(value); //from the first 'then' 最后输出这个 return "from the second 'then'"; }); console.log(p); //Promise{...} 先输出这个
注意,若是 promise 的状态是 resolved 则执行 then参数中的第一个回调函数;若是 promise 的状态是 rejected 则执行 then参数中的第二个回调函数。这个状态是不断传递下来的,这一点和以前的例子相似。
catch(reject) 方法是 then(null, reject)
的别名,在发生错误的时候执行其参数函数:
new Promise(function(resolve, reject){ resolve("resolved"); }).then(function(val){ console.log(val); //resolved throw new Error("man-made Error"); }).catch(function(err){ console.log(err.message); //man-made Error });
错误会从最初的请求沿着回调函数,一直被传递下来。这一点和传统的错误冒泡相似,不管哪里有错误均可以被捕获到:
new Promise(function(resolve, reject){ reject(new Error("original Error")); }).then(function(val){ console.log(val); //不执行 throw new Error("man-made Error"); }).catch(function(err){ console.log(err.message); //original Error });
固然也能够在半路截住错误:
new Promise(function(resolve, reject){ reject(new Error("original Error")); }).then(function(val){ console.log(val); //不执行 throw new Error("man-made Error"); }, function(err){ console.log(`Uncaught Error: ${err.message}`); //Uncaught Error: original Error }).catch(function(err){ console.log(err.message); //不执行 });
这里须要注意如下几点:
这里须要说明的是第4条:错误不会到 Promise 外面是 ES6 规范的说法。具体理解(浏览器环境):控制台依旧会报错,可是不影响 promise 语句以后续代码执行。此外,promise 语句内的异步语句(如事件,定时器等等)抛出的错误,不属于 promise 内部,发生错误会传播出去:
var p = new Promise(function(resolve, reject){ resolve("ok"); setTimeout(function(){throw new Error("setTimeout error")},0); }); p.then(function(val){console.log(val);}); //ok //Uncaught Error: setTimeout error
其次,就以上前两个注意事项举一例说明:
new Promise(function(resolve, reject){ resolve("resolved"); throw "original Error"; //被忽略 }).then(function(val){ console.log(val); //resolved throw (new Error("man-made Error")); }).catch(function(err){ console.log(err.message); //man-made Error });
catch 方法的返回值仍是一个新的 promise 对象,能够继续调用 then 等其余方法:
new Promise(function(resolve, reject){ reject(new Error("reject")); }).catch(function(err){ console.log("1st catch"); //被跳过 return "continue"; }).then(function(val){ console.log(val); //continue });
若是 catch以前没有错误,该 catch 会被跳过。这意味着,catch 不能捕获在其后面的语句中出现的错误:
new Promise(function(resolve, reject){ resolve("resolved"); }).catch(function(err){ console.log("1st catch"); //被跳过 }).then(function(val){ console.log(val); //resolved throw (new Error()); }).catch(function(err){ console.log("2nd catch"); //2nd catch });
finally() 接受一个回调函数(无参数)为参数,和 try...catch...finally 中的 finally 相似,不论 promise 是什么状态,该回调函数都必定会运行。能够用它关闭文件,或者关闭服务器等:
server.listen(0).then(function(){ //do sth. }).finally(server.stop);
finally() 内部实现以下:
Promise.prototype.finally = function(callback){ return this.then( value => {Promise.resolve(callback()).then(() => value)}, error => {Promise.resolve(callback()).then(() => {throw error})} ); };
done() 方法用在 promise 处理语句的末端,用来处理可能未捕获的错误,并抛向全局。若是其带有参数,能够等效为 done() 以前多了一个 then():
p.done(fun1, fun2); //至关于 p.then(fun1,fun2).done();
done() 内部实现以下:
Promise.prototype.done = function(onResolve, onRejected){ this.then(onResolve, onRejected).catch(function(err){ setTimeout(() => {throw err}, 0); }); };
将多个 promise 对象合并成一个新的 promise 实例。其接受一个装仅有 promise 对象的可遍历结构为参数,若是不是 promise 对象,系统会调用 Promise.resolve()
进行类型转换。
promise.all() 方法获得的新的 promise 对象状态由构成它的全部 promise 对象决定,具体分为2种状况:
//伪代码, 因为没有正确的 url var getJSON = function(url){ var promise = new Promise(function(resolve, reject){ var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.response = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler(){ if(client.readyState !== 4) return; if(this.status === 200){ resolve(client.response); } else { reject(new Error(this.statusText)); } } }); return promise; } var pros = ['url1', 'url2', 'url3'].map(url => getJSON(url)); Promise.all(pros).then(function(){ console.log("all successful"); }, function(){ console.log("one rejected"); //one rejected, 因为没有正确的 url });
将多个 promise 对象合并成一个新的 promise 实例。其接受一个装仅有 promise 对象的可遍历结构为参数,若是不是 promise 对象,系统会调用 Promise.resolve()
进行类型转换。
和 promise.all() 不一样的是 Promise.race() 方法获得的新的 promise 对象状态由构成它的 promise 对象中最早改变状态的那一个决定。
//伪代码, 因为没有正确的 url var getJSON = function(url){ var promise = new Promise(function(resolve, reject){ var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.response = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler(){ if(client.readyState !== 4) return; if(this.status === 200){ resolve(client.response); } else { reject(new Error(this.statusText)); } } }); return promise; } //若是5s不能得到数据就报错 var p = Promise.race([ getJSON("url"), new Promise(function(resolve, reject){ setTimeout(() => reject(new Error("Timeout")), 5000); }) ]).then(res => console.log(res)) .catch(err => console.log(err)); //Error, 因为没有正确的 url
将现有对象转化为 promise 对象:
var p = Promise.resolve($.ajax('url')); //jQuery的 $.ajax 方法 //等同于: var p = new Promise(function(resolve){ resolve($.ajax('url')); });
若是传入 Promise.resolve() 的对象不具备 then 方法(ie. unthenable), 则返回一个状态为 resolved 的新 promise 对象。
Promise.resolve("hello").then(function(val){ console.log(val); //hello });
若是你仅仅想获得一个 promise 对象,那利用 resolve() 方法是最简单的:
var promise = Promise.resolve();
Promise.reject(reason)
, 返回一个状态为 rejected 的 promise 实例。参数 reason 会被传递被实例的回调函数。
Promise.reject(new Error("error occured")).catch(err => console.log(err.message)); //error occured
var preloadImage = function(url){ return new Promise(function(resolve, reject){ var image = new Image(); image.onload = resolve; image.onerror = reject; image.src = url; }); };
function getFoo(){ return new Promise(function(resolve){ resolve("foo"); }); } function* gen(){ try{ var foo = yield getFoo(); console.log(foo); } catch(e) { console.log(e); } } var it = gen(); (function go(result){ if(result.done) return result.value; return result.value.then(function(value){ return go(it.next(value)); }, function(err){ return go(it.throw(error)); }); })(it.next()); //foo
const sleep = (time) => new Promise(function(resolve){ setTimeout(resolve, time); }); (async () => { for(var i = 0; i < 5; i++){ await sleep(1000); console.log(new Date, i); } await sleep(1000); console.log(new Date, i); })();