JavaScript Promise 用法


Posted in Javascript onJune 14, 2016

同步编程通常来说易于调试和维护,然而,异步编程通常能获得更好的性能和更大的灵活性。异步的最大特点是无需等待。“Promises”渐渐成为JavaScript里最重要的一部分,大量的新API都开始promise原理实现。下面让我们看一下什么是promise,以及它的API和用法!

Promises现状

XMLHttpRequest API是异步的,但它没有使用promise API。但有很多原生的 javascript API 使用了promise:

*Battery API
*fetch API (XHR的替代品)
*ServiceWorker API

Promises将来只会变得越来越流行、普遍,非常重要,所有的前端开发人员都将用到它。另一个值得注意的是,Node.js是基于Promises的平台(很显然,Promise是它的一个核心特征)。

Promises的用法比你想象的要简单——如果你以前喜欢使用setTimeout来控制异步任务的话!

Promise基本用法

new Promise()构造器可以用在传统的异步任务中,就像以前 setTimeout 和 XMLHttpRequest 的用法一样。一个新的 Promise 使用 new 关键字生成,同时,这个 Promises 提供了 resolve 和 reject 函数让我们执行回调操作:

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 调用转换为 基于 Promises 的任务:

// 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);
});

Promise.resolve() 和 Promise.reject() 可以直接被调用。有时候,当判断出 promise 并不需要真正执行时,我们并不需要 使用 new 创建 Promise 对象,而是可以直接调用 Promise.resolve() 和 Promise.reject()。比如:

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 方法处理返回值!

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

then 回调动作的触发时机是 promise 被执行完。我们还可以串联 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 已经执行完成,单 then 被再次调用时,回调动作将会被再次执行。而如果这个 promise 里执行的是reject 回调函数,这是再调用 then 方法,回调函数将不会被执行。

catch 方法

catch 当一个 promise 被拒绝(reject)时,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 方法里处理执行失败的结果,而在catch 里执行异常结果:

reject(Error('Data could not be found'));

Promise.all 方法

在我们的异步调用时经常有这样一种场景:我们需要同时调用多个异步操作,但希望只有等所有的操作都完成后,我们才去执行响应操作——这就是 Promise.all 的作用。 Promise.all 方法可以接收多个 promise 作为参数,以数组的形式,当这些 promise 都成功执行完成后才调用回调函数。

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!
});

我们还可将fetch和电池状态API混合一起执行,因为它们返回的都是 promise:

Promise.all([fetch('/users.json'), navigator.getBattery()]).then(function(results) {
 // Both promises done!
});

一旦 promise 里调用了reject函数,也就是执行被拒绝了,没有能够正常完成,情况会有些复杂。一旦 promise 被拒绝,catch 方法会捕捉到首个被执行的reject函数:

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.all 是非常重要的接口,将会在很多新诞生的 promise API中扮演重要的作用。

Promise.race

Promise.race 是一个有趣的函数——它不是等待所有的 promise 被resolve 或 reject,而是在所有的 promise 中只要有一个执行结束,它就会触发:

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!

一个有用的场景是,从多个镜像服务器下载资源,一旦有一个返回,其它的返回也就不用处理了。

学会使用 Promises

Promises在过去几年是一个非常火爆的话题,它甚至从JavaScript里抽离出来变成了一个语言架构。相信很快我们将见到有愈来愈多的JavaScript API将使用以promise为基础的模式。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery BS,dialog控件自适应大小
Jul 06 Javascript
JavaScript+CSS控制打印格式示例介绍
Jan 07 Javascript
js数组的基本操作(很全自己整理的)
Oct 16 Javascript
超棒的响应式布局jQuery插件Freetile.js
Nov 17 Javascript
node.js中的fs.write方法使用说明
Dec 15 Javascript
详解JavaScript操作HTML DOM的基本方式
Oct 21 Javascript
基于jquery实现省市联动特效
Dec 17 Javascript
js如何获取网页所有图片
May 12 Javascript
javascript实现QQ空间相册展示源码
Dec 12 Javascript
Mint-UI时间组件起始时间问题及时间插件使用
Aug 20 Javascript
基于JavaScript实现简单抽奖功能代码实例
Oct 20 Javascript
JavaScript实现前端倒计时效果
Feb 09 Javascript
Javascript基础_简单比较undefined和null 值
Jun 14 #Javascript
Javascript缓存API
Jun 14 #Javascript
JS修改地址栏参数实例代码
Jun 14 #Javascript
JS获取地址栏参数的两种方法(简单实用)
Jun 14 #Javascript
Javascript基础_嵌入图像的简单实现
Jun 14 #Javascript
jQuery实例—选项卡的简单实现(js源码和jQuery)
Jun 14 #Javascript
浅析JS获取url中的参数实例代码
Jun 14 #Javascript
You might like
第十节--抽象方法和抽象类
2006/11/16 PHP
php加密解密函数authcode的用法详细解析
2013/10/28 PHP
php一行代码获取文件后缀名实例分析
2014/11/12 PHP
基于laravel Request的所有方法详解
2019/09/29 PHP
PHP7新特性
2021/03/09 PHP
javascript 用原型继承来实现对象系统
2010/03/22 Javascript
基于jQuery的可用于选项卡及幻灯的切换插件
2011/03/28 Javascript
用JS做的简单的可折叠的两级树形菜单
2013/09/21 Javascript
jQuery学习笔记之2个小技巧
2015/01/19 Javascript
JS自定义对象实现Java中Map对象功能的方法
2015/01/20 Javascript
JS实现的论坛Ajax打分效果完整实例
2015/10/31 Javascript
基于jQuery实现点击最后一行实现行自增效果的表格
2016/01/12 Javascript
jQuery+css实现的换页标签栏效果
2016/01/27 Javascript
JQuery fileupload插件实现文件上传功能
2016/03/18 Javascript
jQuery学习笔记之回调函数
2016/08/15 Javascript
jQuery插件HighCharts实现的2D回归直线散点效果示例【附demo源码下载】
2017/03/09 Javascript
vue中各组件之间传递数据的方法示例
2017/07/27 Javascript
JS中数组与对象的遍历方法实例小结
2018/08/14 Javascript
js编写简易的计算器
2020/07/29 Javascript
[06:53]DOTA2每周TOP10 精彩击杀集锦vol.3
2014/06/25 DOTA
基于Python的关键字监控及告警
2017/07/06 Python
LRUCache的实现原理及利用python实现的方法
2017/11/21 Python
理解python中生成器用法
2017/12/20 Python
django 删除数据库表后重新同步的方法
2018/05/27 Python
python 提取tuple类型值中json格式的key值方法
2018/12/31 Python
Python使用正则表达式分割字符串的实现方法
2019/07/16 Python
解决Tensorflow sess.run导致的内存溢出问题
2020/02/05 Python
python求numpy中array按列非零元素的平均值案例
2020/06/08 Python
使用python脚本自动生成K8S-YAML的方法示例
2020/07/12 Python
使用AJAX和Django获取数据的方法实例
2020/10/25 Python
极简鞋类,赤脚的感觉:Lems Shoes
2019/08/06 全球购物
弄虚作假心得体会
2014/09/10 职场文书
民间个人借款协议书
2014/09/30 职场文书
本科毕业论文答辩稿
2015/06/23 职场文书
MySQL性能压力基准测试工具sysbench的使用简介
2021/04/21 MySQL
Java实现二维数组和稀疏数组之间的转换
2021/06/27 Java/Android