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 相关文章推荐
JqueryMobile动态生成listView并实现刷新的两种方法
Mar 05 Javascript
js拖拽一些常见的思路方法整理
Mar 19 Javascript
JavaScript中获取高度和宽度函数总结
Oct 08 Javascript
jQuery使用Selectator插件实现多选下拉列表过滤框(附源码下载)
Apr 08 Javascript
JS中递归函数
Jun 17 Javascript
JavaScript实现复制文章自动添加版权
Aug 02 Javascript
详解vue.js移动端导航navigationbar的封装
Jul 05 Javascript
jQuery+Ajax请求本地数据加载商品列表页并跳转详情页的实现方法
Jul 12 jQuery
vue 简单自动补全的输入框的示例
Mar 12 Javascript
element vue validate验证名称重复 输入框与后台重复验证 特殊字符 字符长度 及注意事项小结【实例代码】
Nov 20 Javascript
vue实现两个组件之间数据共享和修改操作
Nov 12 Javascript
Vue中computed和watch有哪些区别
Dec 19 Vue.js
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
星际玩家的三大定律
2020/03/04 星际争霸
php加密解密函数authcode的用法详细解析
2013/10/28 PHP
PHP中date与gmdate的区别及默认时区设置
2014/05/12 PHP
php封装的验证码工具类完整实例
2016/10/19 PHP
jQuery的实现原理的模拟代码 -3 事件处理
2010/08/03 Javascript
Javascript 绘制 sin 曲线过程附图
2014/08/21 Javascript
自定义jQuery插件方式实现强制对象重绘的方法
2015/03/23 Javascript
Node.js开启Https的实践详解
2016/10/25 Javascript
深入理解JavaScript中的块级作用域、私有变量与模块模式
2016/10/31 Javascript
Ajax跨域实现代码(后台jsp)
2017/01/21 Javascript
认识less和webstrom的less配置方法
2017/08/02 Javascript
vue项目中,main.js,App.vue,index.html的调用方法
2018/09/20 Javascript
countUp.js实现数字滚动效果
2019/10/18 Javascript
vue vantUI tab切换时 list组件不触发load事件的问题及解决方法
2020/02/14 Javascript
浅析JavaScript 函数柯里化
2020/09/08 Javascript
Python实现的几个常用排序算法实例
2014/06/16 Python
简化Python的Django框架代码的一些示例
2015/04/20 Python
Python中的os.path路径模块中的操作方法总结
2016/07/07 Python
python 转换 Javascript %u 字符串为python unicode的代码
2016/09/06 Python
python 巧用正则寻找字符串中的特定字符的位置方法
2018/05/02 Python
django加载本地html的方法
2018/05/27 Python
python下载库的步骤方法
2019/10/12 Python
Python unittest单元测试框架及断言方法
2020/04/15 Python
Django使用django-simple-captcha做验证码的实现示例
2021/01/07 Python
Vans英国官方网站:美国南加州的原创极限运动潮牌
2017/01/20 全球购物
师范生自我鉴定范文
2013/10/05 职场文书
汽车专业毕业生推荐信
2013/11/12 职场文书
农村婚礼证婚词
2014/01/08 职场文书
关于环保的建议书400字
2014/03/12 职场文书
理财学专业自荐书
2014/06/28 职场文书
电子专业自荐信
2014/07/01 职场文书
先进典型事迹材料
2014/12/29 职场文书
Mysql 如何实现多张无关联表查询数据并分页
2021/06/05 MySQL
Redis之RedisTemplate配置方式(序列和反序列化)
2022/03/13 Redis
Django中celery的使用项目实例
2022/07/07 Python
MySQL自定义函数及触发器
2022/08/05 MySQL