Promise 的作用:解决回调问题,为异步操作提供统一的接口,还可以链式调用。
原理:先定义好需要回调的多个函数,然后在每个函数内部“发射(传递)”出下一个函数需要的数据,然后通过链式调用来执行。如果传递的是reject值则停止。
注意:Promise 新建后立即执行,然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行。
let promise = new Promise(function(resolve, reject) { console.log('Promise'); resolve(); }); promise.then(function() { console.log('resolved.'); }); console.log('Hi!');
|
Promise有三种状态:pending、resolved(fulfilled)、rejected
Promise是一个构造函数,有以下API:
function Promise(resolver) {} Promise.prototype.then = function(resolve,reject) { } Promise.prototype.catch = function(rejected) { } Promise.resolve = function() { } Promise.reject = function() { } Promise.all = function() { } Promise.race = function() { }
|
最佳实践:
一般总是建议,Promise 对象后面要跟catch方法,这样可以处理 Promise 内部发生的错误。catch方法返回的还是一个 Promise 对象,因此后面还可以接着调用then方法。
简单示例:
var promise = new Promise(function(resolve, reject) { if (){ resolve(value); } else { reject(error); } }); promise.then(function(value) { }, function(value) { }); promise.then(function(value) { }).then(function(value){ });
|
实际例子:假设下边一个场景,我们一个服务,从一个外边service获取数据,然后写到一个db里,或者一个存储里,最后在把存储的状态龙出来,那么如果没有promise是怎么写的呢?可能会是这样:
getData(function (value1) { storeToDb(value1, function(value2) { logStore(value2, function(value3) { }); }); });
|
Promise实现:
function getData(){ return new Promise((resolve,reject) =>{ if(){ resolve(data) }else{ reject(err) } }) } function storeData(data){ return new Promise((resolve,reject)=>{ if(){ resolve(data) }else{ reject(err) } }) } getData() .then(data => storeData(data)) .then(data => console.log('the process is done',data)); .catch(err => console.error('there is the err',err));
|
这样写是不是就是很清楚了,先getData,然后再storeData,最后将这次运行的情况log了出来,其中有任何的问题,在catch中都可以Catch出来。
利用Promise封装一个ajax:
var url='xxx'; function getJSON(url) { return new Promise(function(resolve, reject) { var handler = function() { if (this.readyState != 4){ return } if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } } var XHR = new XMLHttpRequest(); XHR.open('GET', url, true); XHR.onreadystatechange = handler; XHR.responseType = "json"; XHR.setRequestHeader("Accept", "application/json"); XHR.send(); }) } getJSON("/posts.json").then(function(json) { console.log('Contents: ' + json); }, function(error) { console.error('出错了', error); });
|
封装一个加载图片的操作:一旦加载完成,Promise的状态就发生改变
var preloadImage = function (path) { return new Promise(function (resolve, reject) { var image = new Image(); image.onload = resolve; image.onerror = reject; image.src = path; }); };
|
Promise.all([fn1,fn2…]):
Promise.all接收一个Promise对象组成的数组作为参数,当这个数组所有的Promise对象状态都变成resolved或者rejected的时候,它才会去调用then方法。
var url1 = 'aaa'; var url2 = 'bbb'; function renderAll() { return Promise.all([getJSON(url1), getJSON(url2)]); } renderAll().then(function(value) { console.log(value); })
|
Promise.race([fn1,fn2…]):
Promise.race都是以一个Promise对象组成的数组作为参数,不同的是,只要当数组中的其中一个Promsie状态变成resolved或者rejected时,就可以调用.then方法了。
function renderRace() { return Promise.race([getJSON(url1), getJSON(url2)]); } renderRace().then(function(value) { console.log(value); })
|
有用的附加方法
done():用于捕获错误到全局
asyncFunc() .then(f1) .catch(r1) .then(f2) .done(); Promise.prototype.done = function (onFulfilled, onRejected) { this.then(onFulfilled, onRejected) .catch(function (reason) { setTimeout(() => { throw reason }, 0); }); };
|
从上面代码可见,done方法的使用,可以像then方法那样用,提供fulfilled和rejected状态的回调函数,也可以不提供任何参数。但不管怎样,done都会捕捉到任何可能出现的错误,并向全局抛出。
finally():用于不管前面的结果如何,接下来都要执行的操作
server.listen(0) .then(function () { }) .finally(server.stop); Promise.prototype.finally = function (callback) { let P = this.constructor; return this.then( value => P.resolve(callback()).then(() => value), reason => P.resolve(callback()).then(() => { throw reason }) ); };
|
上面代码中,不管前面的 Promise 是fulfilled还是rejected,都会执行回调函数callback。
async
async 是ES7标准提出的函数,async 函数返回值是 Promise 对象,可以直接使用 then() 方法进行调用。
async 函数的 await 命令后面则可以是 Promise 或者 原始类型的值(Number,string,boolean,但这时等同于同步操作)。
await 会等待 Promise 完成,并返回 Promise 的结果,然后再执行后面的代码。同时只能用在 async 函数中使用。
function timeout(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } async function asyncPrint(value, ms) { await timeout(ms); console.log(ms); } asyncPrint('hello world', 5000);
|
正常情况下,await 命令后面跟着的是 Promise ,如果不是的话,也会被转换成一个 立即 resolve 的 Promise。
使用:
function getData(){ console.log("这里是返回的数据") } async function test(){ return getData() }
|
async 函数返回的 Promise 对象,必须等到内部所有的 await 命令的 Promise 对象执行完,才会发生状态改变
const delay = timeout => new Promise(resolve=> setTimeout(resolve, timeout)); async function f(){ await delay(1000); await delay(2000); await delay(3000); return 'done'; } f().then(v => console.log(v));
|
参考:http://es6.ruanyifeng.com/#docs/promise