JavaScript Promises

版权声明:此文章转载自极客头条

如需转载请联系听云College团队成员小尹 邮箱:yinhy#tingyun.com


尽管同步代码很容易跟踪调试,异步代码在性能和灵活性上更胜一筹。如果你能立刻触发多个请求并在它们各自准备好之后处理,那岂不是很棒。Promises实现了很多蕴含Promise哲学的API,其正在成为JavaScript世界中的重要成员。让我们看一看Promises,它的API以及如果使用。

Promises

XMLHttpRequest API是异步的,但没有使用Promises API。然而,这里有一些目前使用Promises的原生APIs。:

Battery API

fetch API (XHR's 的替代品)

ServiceWorker API (很快就会公布)

romises趋于流行,所以每个前端开发者熟悉它很重要。很有必要知道Node.js是Promises的另一个平台(显然Promise是语言核心特性)

测试Promises比你想的还简单,因为setTimeout可以被当做你的异步任务。

Promise基础用法

使用new关键字创建一个Promise,Promise提供resolve和reject函数用于回调:

var p = new Promise(function(resolve, reject) {
  // Do some processing
  if(/* good condition */) {
    resolve('Success!');
  }
  else {
    reject('Failure!');
  }
});

基于开发者执行的任务结果,由他们手动的回调resolve或reject函数。同时注意你在处理异步方法时,不必总是在promise内完全实现异步任务,使用promise的优势在于你可以指望promise指定的函数。举个例子:

var userCache = {};
// There's a chance an async action may take place
// i.e. if the user isn't in cache, use fetch to get their info
// This is a perfect function to return a promise from
function getUserDetail(username) {
  return new Promise(function(resolve, reject) {
    // Not async but since we're in a promise we can resolve ourself
    if(userCache[username]) {
      resolve(userCache[username]);
    }
    else {
      // Use the fetch API to get the information
      fetch('users/' + username + '.json')
        .then(function(result) {
          userCache[username] = result;
          resolve(result);
        })
        .catch(function() {
          reject(Error('Could not find user: ' + username));
        });
    }
  });
}
// Use the promise 
getUserDetail('davidwalsh')
.then(function(userInfo) {
  // Do something with the user info
})
.catch(function(err) {
  // Do something with the error
});

因为总是返回一个promise,所以你可以对返回值使用then和catch方法。

then方法

所有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调用resolve方法后触发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方法接收前一个的返回值。

catch方法

catch在promise执行reject方法之后执行。

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加载:某时触发了很多异步的交互,但你想等它们全部完成再进行响应 -- 这就需要Promise.all函数了。Promise.all函数操作一个promise列表,在所有promise处理完后执行一个回调。

Promise.all([promise1, promise2]).then(function(result1, result2) {
  // Both promises resolved
})
.catch(function(error) {
  // One or more promises was rejected
});

一个思考Promise.all的好方式就是同时发送多个AJAX请求:

var request1 = fetch('/users.json');
var request2 = fetch('/articles.json');
Promise.all([request1, request2]).then(function(response1, response2) {
  // Both promises done!
});

你可以组合使用fetch方法和Battery API,因为他们都返回promise:

var request2 = fetch('/articles.json');
Promise.all([fetch('/users.json'), navigator.getBattery()]).then(function(response, battery) {
  // Both promises done!
});

处理一个rejection是很困难的。如果promise中有任何一个reject,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(one, two) {
  console.log('Then: ', one);
}).catch(function(err) {
  console.log('Catch: ', err);
});
// From the console:
// Then: Second!

当API趋向promise时,Promise.all会更加有用。

Promise.race方法

Promise.race是个很有趣的函数 -- 和等待所有promise处理完不同,Promise.race在promise列表中任意一个promise处理完就立刻被调用:

var req1 = new Promise(function(resolve, reject) { 
  setTimeout(function() { resolve('First!'); }, 8000);
});
var req2 = new Promise(function(resolve, reject) { 
  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!

一个用例是绑定主要资源请求和备份资源请求(防止主要或备份不可用)

习惯Promises

Promises是过去几年(如果你是Dojo Toolkit用户,那么十年)的热门话题,它从JavaScript框架模式演变成语言产品。你很可能将会看到很多新的JavaScript APIs用promise模式实现...

...这是一件很棒的事!开发者可以绕开回调地狱,异步交互可以像其他变量一样分发。Promises作为一种工具需要一段时间去适应,是时候开始学习它了!

想阅读更多技术文章,请访问听云技术博客,访问听云官方网站感受更多应用性能优化魔力。

关于作者

郝淼emily

重新开始,从心开始

我要评论

评论请先登录,或注册