JavaScript中Promise的使用详解


Posted in Javascript onFebruary 26, 2017

Promise是ES6中的函数,规范了如何处理异步任务的回调函数,功能类似于jQuery的defferred。简单说就是通过promise对象的不同状态调用不同的回调函数。目前IE8及以下不支持,其他浏览器都支持。

promise对象的状态,从Pending转换为Resolved或Rejected之后,这个promise对象的状态就不会再发生任何变化。

使用步骤:

var promise = new Promise(function(resolve, reject) {

 // 异步任务,通过调用resolve(value) 或 reject(error),以改变promise对象的状态;改变状态的方法只能在此调用。

//promise状态改变后,会调用对应的回调方法

});

promise.then(function(value){//resolve时的回调函数,参数由异步的函数传进来})

.catch(function(error){//发生异常时或明确reject()时的回调函数})

 具体使用:

function getURL(URL) {           //因为promise创建时即执行,所以用工厂函数封装promise对象
  return new Promise(function (resolve, reject) {
    var req = new XMLHttpRequest();
    req.open('GET', URL, true);
    req.onload = function () {
      if (req.status === 200) {
        resolve(req.responseText);
      } else {
        reject(new Error(req.statusText));
      }
    };
    req.onerror = function () {
      reject(new Error(req.statusText));
    };
    req.send();
  });
}
// 运行示例
var URL = "http://httpbin.org/get";
getURL(URL).then(function onFulfilled(value){
  console.log(value);
}).catch(function onRejected(error){
  console.error(error);
});

Promise的回调只有异步方式,即使是同步任务的回调也是异步执行 。

var promise = new Promise(function (resolve){
  console.log("inner promise");         // 执行1:同步任务先执行
  resolve(‘callBack');
});
promise.then(function(value){
  console.log(value);              // 执行3:虽然注册时状态为resolved,但回调仍是异步的;
});
console.log("outer promise");          // 执行2:同步代码先执行

 

promise的方法链

then方法注册的回调会依次被调用,每个then方法之间通过return 返回值传递参数。但是回调中的异常会导致跳过之间then的回调,直接调用catch的回调,之后再继续调用剩下的then的回调。在then(onFulfilled, onRejected)中,onFulfilled的异常不会被自己的onRejected捕获,所以优先使用catch。

 promise .then(taskA) .then(taskB) .catch(onRejected) .then(finalTask);

 taskA抛异常,taskB被跳过,finalTask仍会被调用,因为catch返回的promise对象的状态为resolved。

then方法内可以返回3种值

1. 返回另一个promise对象,下一个then方法根据其状态选择onFullfilled/onRejected回调函数执行,参数仍由新promise的resolv/reject方法传递;

2. 返回一个同步值,下一个then方法沿用当前promise对象的状态,无需等异步任务结束会立即执行;实参为上一then的返回值;如果没有return,则默认返回undefined;

3. 抛出异常(同步/异步):throw new Error(‘xxx');

then不仅是注册一个回调函数,还会将回调函数的返回值进行变换,创建并返回一个新promise对象。实际上Promise在方法链中的操作的都不是同一个promise对象。

var aPromise = new Promise(function (resolve) {
  resolve(100);
});
var thenPromise = aPromise.then(function (value) {
  console.log(value);
});
var catchPromise = thenPromise.catch(function (error) {
  console.error(error);
});
console.log(aPromise !== thenPromise); // => true
console.log(thenPromise !== catchPromise);// => true

Promise.all()静态方法,同时进行多个异步任务。在接收到的所有promise对象都变为FulFilled 或者Rejected 状态之后才会继续进行后面的处理。

Promise.all([promiseA, promiseB]).then(function(results){//results是个数组,元素值和前面promises对象对应});

// 由promise对象组成的数组会同时执行,而不是一个一个顺序执行,开始时间基本相同。
function timerPromisefy(delay) {
  console.log('开始时间:”'+Date.now()) 
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve(delay);
    }, delay);
  });
}
var startDate = Date.now();
Promise.all([
  timerPromisefy(100),    //promise用工厂形式包装一下
  timerPromisefy(200),
  timerPromisefy(300),
  timerPromisefy(400)
]).then(function (values) {
  console.log(values);  // [100,200,300,400]
});

不同时执行,而是一个接着一个执行promise

//promise factories返回promise对象,只有当前异步任务结束时才执行下一个then
function sequentialize(promiseFactories) {
  var chain = Promise.resolve();
  promiseFactories.forEach(function (promiseFactory) {
    chain = chain.then(promiseFactory);
  });
  return chain;
}

Promise.race()同all()类似,但是race()只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会执行对应的回调函数。不过在第一个promise对象变为Fulfilled之后,并不影响其他promise对象的继续执行。

//沿用Promise.all()的例子
Promise.race([
  timerPromisefy(1),
  timerPromisefy(32),
  timerPromisefy(64),
  timerPromisefy(128)
]).then(function (value) {
  console.log(values);  // [1]
});

Promise.race()作为定时器的妙用

Promise.race([
  new Promise(function (resolve, reject) {
    setTimeout(reject, 5000);     // timeout after 5 secs
  }),
  doSomethingThatMayTakeAwhile()
]);

在then中改变promise状态

因为then的回调中只有value参数,没有改变状态的方法(只能在构造方法的异步任务中使用),要想改变传给下一个then的promise对象的状态,只能重新new一个新的Promise对象,在异步任务中判断是否改变状态,最后return出去传给下一个then/catch。

var promise = Promise.resolve(‘xxx');//创建promise对象的简介方法
promise.then(function (value) {
  var pms=new Promise(function(resolve,reject){
    setTimeout(function () {
      // 在此可以判断是否改变状态reject/resolve
      Reject(‘args');
    }, 1000);
  })
  return pms;  //该promise对象可以具有新状态,下一个then/catch需要等异步结束才会执行回调;如果返回普通值/undefined,之后的then/catch会立即执行
}).catch(function (error) {
  // 被reject时调用
  console.log(error)
});

获取两个promises的结果

//方法1:通过在外层的变量传递
var user;
getUserByName('nolan').then(function (result) {
  user = result;
  return getUserAccountById(user.id);
}).then(function (userAccount) {
  //可以访问user和userAccount
});

//方法2:后一个then方法提到前一个回调中
getUserByName('nolan').then(function (user) {
  return getUserAccountById(user.id).then(function (userAccount) {
    //可以访问user和userAccount
  });
});

 

注意使用promise时的整体结构

假定doSomething()和doSomethingElse()都返回了promise对象

常用方式:

doSomething().then(doSomethingElse).then(finalHandler);
doSomething
|-----------------|
         doSomethingElse(resultOfDoSomething)  //返回新promise,下一个then要收到新状态才执行
         |------------------|
                   finalHandler(resultOfDoSomethingElse)
                   |---------------------|

常用变通方式:

doSomething().then(function () { return doSomethingElse();}).then(finalHandler);
doSomething
|-----------------|
         doSomethingElse(undefined) //then外层函数的arguments[0]== resultOfDoSomething
         |------------------|
                   finalHandler(resultOfDoSomethingElse)
                   |------------------|

错误方式1:

doSomething().then(function () { doSomethingElse();}).then(finalHandler);
doSomething
|-----------------|
         doSomethingElse(undefined)  //虽然doSomethingElse会返回promise对象,但最外层的回调函数是return undefined,所以下一个then方法无需等待新promise的状态,会马上执行回调。
         |------------------|
         finalHandler(undefined)
         |------------------|

错误方式2:

doSomething().then(doSomethingElse()).then(finalHandler);
doSomething
|-----------------|
doSomethingElse(undefined)     //回调函数在注册时就直接被调用
|----------|
         finalHandler(resultOfDoSomething)
         |------------------|

 

Javascript 相关文章推荐
33种Javascript 表格排序控件收集
Dec 03 Javascript
javascript 词法作用域和闭包分析说明
Aug 12 Javascript
jQuery获取Select选择的Text和Value(详细汇总)
Jan 25 Javascript
js 获取后台的字段 改变 checkbox的被选中的状态 代码
Jun 05 Javascript
JS性能优化笔记搜索整理
Aug 21 Javascript
浅析JavaScript中的CSS属性及命名规范
Nov 28 Javascript
使用js如何实现全选与全不选
Dec 30 Javascript
Bootstrap CSS组件之分页(pagination)和翻页(pager)
Dec 17 Javascript
浅析jsopn跨域请求原理及cors(跨域资源共享)的完美解决方法
Feb 06 Javascript
Canvas + JavaScript 制作图片粒子效果
Feb 08 Javascript
jQuery ajax请求struts action实现异步刷新
Apr 19 jQuery
vue实现登录页面的验证码以及验证过程解析(面向新手)
Aug 02 Javascript
setTimeout函数的神奇使用
Feb 26 #Javascript
node.js入门学习之url模块
Feb 25 #Javascript
从零学习node.js之利用express搭建简易论坛(七)
Feb 25 #Javascript
从零学习node.js之express入门(六)
Feb 25 #Javascript
Node.JS中事件轮询(Event Loop)的解析
Feb 25 #Javascript
走进javascript——不起眼的基础,值和分号
Feb 24 #Javascript
angular.js 路由及页面传参示例
Feb 24 #Javascript
You might like
一些关于PHP的知识
2006/11/17 PHP
php XMLWriter类的简单示例代码(RSS输出)
2011/09/30 PHP
php删除指定目录的方法
2015/04/03 PHP
jquery获取多个checkbox的值异步提交给php的方法
2015/06/24 PHP
PHP如何根据文件头检测文件类型实例代码
2018/10/14 PHP
解决Laravel 使用insert插入数据,字段created_at为0000的问题
2019/10/11 PHP
大家未必知道的Js技巧收藏
2008/04/07 Javascript
Domino中运用jQuery读取视图内容的方法
2009/10/21 Javascript
模仿百度三维地图的js数据分享
2011/05/12 Javascript
JavaScript面向对象设计二 构造函数模式
2011/12/20 Javascript
ExtJS中文乱码之GBK格式编码解决方案及代码
2013/01/20 Javascript
Javascript写入txt和读取txt文件示例
2014/02/12 Javascript
分享20款美化网站的 jQuery Lightbox 灯箱插件
2014/10/10 Javascript
Node.js的项目构建工具Grunt的安装与配置教程
2016/05/12 Javascript
AngularJS 服务详细讲解及示例代码
2016/08/17 Javascript
详解webpack编译多页面vue项目的配置问题
2017/12/11 Javascript
vue实现未登录跳转到登录页面的方法
2018/07/17 Javascript
vue中el-upload上传图片到七牛的示例代码
2018/10/19 Javascript
微信公众号平台接口开发 获取微信服务器IP地址方法解析
2019/08/14 Javascript
JS中类的静态方法,静态变量,实例方法,实例变量区别与用法实例分析
2020/03/14 Javascript
ES6 十大特性简介
2020/12/09 Javascript
Django教程笔记之中间件middleware详解
2018/08/01 Python
Pycharm无法显示动态图片的解决方法
2018/10/28 Python
关于PyTorch源码解读之torchvision.models
2019/08/17 Python
python绘制无向图度分布曲线示例
2019/11/22 Python
15个应该掌握的Jupyter Notebook使用技巧(小结)
2020/09/23 Python
一款CSS3实现多功能下拉菜单(带分享按)的教程
2014/11/05 HTML / CSS
墨西哥网上超市:Superama
2018/07/10 全球购物
社区学习十八大感想
2014/01/22 职场文书
标准化管理实施方案
2014/02/25 职场文书
小露珠教学反思
2014/04/30 职场文书
公安领导班子四风问题个人整改措施思想汇报
2014/10/09 职场文书
撤诉状格式范本
2015/05/19 职场文书
咖啡厅里的创业计划书
2019/08/21 职场文书
浅谈golang package中init方法的多处定义及运行顺序问题
2021/05/06 Golang
Centos环境下Postgresql 安装配置及环境变量配置技巧
2021/05/18 PostgreSQL