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 相关文章推荐
JS 日期验证正则附asp日期格式化函数
Sep 11 Javascript
数组Array进行原型prototype扩展后带来的for in遍历问题
Feb 07 Javascript
浅析2种JavaScript继承方式
Dec 04 Javascript
AngularJS入门教程之AngularJS表达式
Apr 18 Javascript
利用JQuery直接调用asp.net后台的简单方法
Oct 27 Javascript
jquery select2的使用心得(推荐)
Dec 04 Javascript
微信小程序使用slider设置数据值及switch开关组件功能【附源码下载】
Dec 09 Javascript
原生JS实现的双色球功能示例
Feb 02 Javascript
基于bootstrap页面渲染的问题解决方法
Aug 09 Javascript
微信jssdk逻辑在vue中的运用详解
Nov 14 Javascript
JS中注入eval, Function等系统函数截获动态代码
Apr 03 Javascript
微信小程序 Storage更新详解
Jul 16 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
不用数据库的多用户文件自由上传投票系统(3)
2006/10/09 PHP
支持中文的PHP按字符串长度分割成数组代码
2015/05/17 PHP
php 实现Hash表功能实例详解
2016/11/29 PHP
thinkphp 手机号和用户名同时登录
2017/01/20 PHP
javascript 鼠标拖动图标技术
2010/02/07 Javascript
基于jQuery的Tab选项框效果代码(插件)
2011/03/01 Javascript
浅析JavaScript中的CSS属性及命名规范
2013/11/28 Javascript
jquery 使用简明教程
2014/03/05 Javascript
AngularJS模块详解及示例代码
2016/08/17 Javascript
input 禁止输入特殊字符的四种实现方式
2016/08/24 Javascript
详谈Angular路由与Nodejs路由的区别
2017/03/05 NodeJs
微信小程序--onShareAppMessage分享参数用处(页面分享)
2017/04/18 Javascript
浅谈vue.js中v-for循环渲染
2017/07/26 Javascript
深入理解vue-router之keep-alive
2017/08/31 Javascript
从vue源码解析Vue.set()和this.$set()
2018/08/30 Javascript
详解关于element el-button使用$attrs的一个注意要点
2018/11/09 Javascript
浅析vue 函数配置项watch及函数 $watch 源码分享
2018/11/22 Javascript
JavaScript解析及序列化JSON的方法实例分析
2019/01/04 Javascript
微信小程序第三方框架对比 之 wepy / mpvue / taro
2019/04/10 Javascript
JavaScript 实现轮播图特效的示例
2020/11/05 Javascript
vue 数据操作相关总结
2020/12/17 Vue.js
[59:30]VG vs LGD 2019国际邀请赛淘汰赛 胜者组 BO3 第二场 8.22
2019/09/05 DOTA
Python实现遍历数据库并获取key的值
2015/05/17 Python
使用Python写CUDA程序的方法
2017/03/27 Python
Python的mysql数据库的更新如何实现
2017/07/31 Python
python全局变量引用与修改过程解析
2020/01/07 Python
keras的backend 设置 tensorflow,theano操作
2020/06/30 Python
Kmeans均值聚类算法原理以及Python如何实现
2020/09/26 Python
Django web自定义通用权限控制实现方法
2020/11/24 Python
css3中background新增的4个新的相关属性用法介绍
2013/09/26 HTML / CSS
GafasWorld哥伦比亚:网上购买眼镜
2017/11/28 全球购物
Hashtable 添加内容的方式有哪几种,有什么区别?
2012/04/08 面试题
港湾网络笔试题
2014/04/19 面试题
物业客服专员岗位职责
2013/11/30 职场文书
分公司总经理岗位职责
2014/07/30 职场文书
初中家长评语和期望
2014/12/26 职场文书