尽管同步代码易于追踪和调试,但异步代码广泛在性能和灵活性上更具优点。Why "hold up the show" when you can trigger numerous requests at once and then handle them when each is ready?(这句要怎么翻??)promise和许多基于promise的新的API已经成为JavaScript世界重要的一部分。让咱们来看一下promise的API如何来使用。javascript
XMLHttpRequest API是异步的但它并无用Promises API,如今有一些native APIs正在使用promises:html
Battery API(译者注:这篇文章我也有翻译)前端
fetch API(XHR的取代者)html5
ServiceWorker API(关于这个API的文章正在路上)java
promises会变得很流行,因此前端开发者们都应该去学习它。毫无疑问,Node.js是promises另外一个重要的平台(显然,promises是一个核心语法特性)。es6
测试 promises 比你想得容易得多,由于 setTimeout
能够模拟你的异步“任务”json
构造函数 new Promise()
应该只用于老式的异步任务,好比 setTimeout
或者 XMLHttpRequest
。咱们使用 new
关键字和promise提供的 resolve
和 reject
回调函数来建立新的 promise:api
var p = new Promise(function(resolve, reject) { // Do an async task async task and then... if(/* good condition */) { resolve('Success!'); } else { reject('Failure!'); } }); p.then(function() { /* do something with the result */ }).catch(function() { /* error :( */ })
根据异步任务的返回结果,开发者能够在回调函数体的内部手动调用 resolve
或者 reject
。把 XMLHttpRequest 转化为基于promise的任务就是一个实际的例子:数组
// From Jake Archibald's Promises and Back: // http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest function get(url) { // Return a new promise. return new Promise(function(resolve, reject) { // Do the usual XHR stuff var req = new XMLHttpRequest(); req.open('GET', url); req.onload = function() { // This is called even on 404 etc // so check the status if (req.status == 200) { // Resolve the promise with the response text resolve(req.response); } else { // Otherwise reject with the status text // which will hopefully be a meaningful error reject(Error(req.statusText)); } }; // Handle network errors req.onerror = function() { reject(Error("Network Error")); }; // Make the request req.send(); }); } // Use it! get('story.json').then(function(response) { console.log("Success!", response); }, function(error) { console.error("Failed!", error); });
Sometimes you don't need to complete an async tasks within the promise -- if it's possible that an async action will be taken, however, returning a promise will be best so that you can always count on a promise coming out of a given function.(译者注:求你们帮忙翻译。。)这样你就能够简单地调用Promise.resolve()
或者 Promise.reject()
而不须要使用 new
关键字。举例说明:promise
var userCache = {}; function getUserDetail(username) { // In both cases, cached or not, a promise will be returned if (userCache[username]) { // Return a promise without the "new" keyword return Promise.resolve(userCache[username]); } // Use the fetch API to get the information // fetch returns a promise return fetch('users/' + username + '.json') .then(function(result) { userCache[username] = result; return result; }) .catch(function() { throw new Error('Could not find user: ' + username); }); }
上面的函数老是返回一个promise对象,你老是能够对这个返回值使用then
或者 catch
方法。
全部的promise实例都有 then
方法,来对这个promise实例作进一步处理。第一个 then
方法的回调函数接收 resolve()
方法里的值:
new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve(10); }, 3000); }) .then(function(result) { console.log(result); }); // From the console: // 10
当promise被resolved时,会调用then
回调方法。你也能够链式调用 then
方法:
new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve(10); }, 3000); }) .then(function(num) { console.log('first then: ', num); return num * 2; }) .then(function(num) { console.log('second then: ', num); return num * 2; }) .then(function(num) { console.log('last then: ', num);}); // From the console: // first then: 10 // second then: 20 // last then: 40
每个 then
方法接收到上一个 then
方法的返回值。
若是一个promise已经被resolved,then
方法有一次被调用,那么回调函数马上执行。若是promise被rejected,而then
方法在rejection以后,那回调函数永远不会被执行。
当promise被rejected时,catch
回调函数被执行:
new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { reject('Done!'); }, 3000); }) .then(function(e) { console.log('done', e); }) .catch(function(e) { console.log('catch: ', e); }); // From the console: // 'catch: Done!'
在reject
方法里执行什么内容取决于你。一个常见的模式是发送一个Error
到catch
方法中:
reject(Error('Data could not be found'));
Promise.all
想一下JavaScript loaders:有许多个异步任务被同时触发,但你只想在它们都完成以后才作出回应---这就是Promise.all
的由来。Promise.all
方法传入一个promise的数组,而后在它们所有resolved以后再出发回调函数:
Promise.all([promise1, promise2]).then(function(results) { // Both promises resolved }) .catch(function(error) { // One or more promises was rejected });
一个完美的想象Promise.all
方法做用的例子就是一次性出发多个AJAX(经过 fetch
):
var request1 = fetch('/users.json'); var request2 = fetch('/articles.json'); Promise.all([request1, request2]).then(function(results) { // Both promises done! });
你能够合并都返回promise的APIs,好比fetch
和 Battery API:
Promise.all([fetch('/users.json'), navigator.getBattery()]).then(function(results) { // Both promises done! });
固然,处理rejection是困难的。若是数组中有任意一个promise被rejected,catch
方法就被触发,而且接收第一个rejection:
var req1 = new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve('First!'); }, 4000); }); var req2 = new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { reject('Second!'); }, 3000); }); Promise.all([req1, req2]).then(function(results) { console.log('Then: ', one); }).catch(function(err) { console.log('Catch: ', err); }); // From the console: // Catch: Second!
Promise.race
Promise.race
是个有趣的方法---不是等待全部的promise被resolved或者rejected,而是只要数组中有一个promise被resolved或者rejected,Promise.race
方法就被触发:
var req1 = new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve('First!'); }, 8000); }); var req2 = new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve('Second!'); }, 3000); }); Promise.race([req1, req2]).then(function(one) { console.log('Then: ', one); }).catch(function(one, two) { console.log('Catch: ', one); }); // From the console: // Then: Second!
一个颇有用的例子就是在有第一资源请求和第二资源请求时能够用Promise.race
。
在过去的几年中,promise一直是一个热点话题(若是你是Dojo Toolkit用户的话,会是过去的10年间),如今promise已经从JavaScript框架特性的级别变成了JavaScript语言的特性。你将看到会有愈来愈多的API会基于promise来实现,这是一个很好的事情!开发者们能够避免之前的callback hell,而且异步interactions能够像其余的变量同样互相传递。promise花了很长的时间变成如今的样子,而如今是应该学习promise的时候了。