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 相关文章推荐
jquery实现的网页自动播放声音
Apr 30 Javascript
js从Cookies里面取值的简单实现
Jun 30 Javascript
自编jQuery插件实现模拟alert和confirm
Sep 01 Javascript
jQuery中wrapInner()方法用法实例
Jan 16 Javascript
使用jQuery判断Div是否在可视区域的方法 判断div是否可见
Feb 17 Javascript
jquery遍历标签中自定义的属性方法
Sep 17 Javascript
微信小程序城市定位的实现实例(获取当前所在国家城市信息)
May 17 Javascript
vue动态路由实现多级嵌套面包屑的思路与方法
Aug 16 Javascript
详解Vue.js组件可复用性的混合(mixin)方式和自定义指令
Sep 06 Javascript
JS实现处理时间,年月日,星期的公共方法示例
May 31 Javascript
微信小程序12行js代码自己写个滑块功能(推荐)
Jul 15 Javascript
react中useState使用:如何实现在当前表格直接更改数据
Aug 05 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
ThinkPHP的URL重写问题
2014/06/22 PHP
PHP 双链表(SplDoublyLinkedList)简介和使用实例
2015/05/12 PHP
php正则表达式基本知识与应用详解【经典教程】
2017/04/17 PHP
PHP设计模式(六)桥连模式Bridge实例详解【结构型】
2020/05/02 PHP
js监听表单value的修改同步问题,跨浏览器支持
2009/12/31 Javascript
JavaScript高级程序设计 阅读笔记(四) ECMAScript中的类型转换
2012/02/27 Javascript
node.js 使用ejs模板引擎时后缀换成.html
2015/04/22 Javascript
干货分享:让你分分钟学会javascript闭包
2015/12/25 Javascript
JQuery为元素添加样式的实现方法
2016/07/20 Javascript
利用AngularJs实现京东首页轮播图效果
2016/09/08 Javascript
AngularJS+Bootstrap实现多文件上传与管理
2016/11/08 Javascript
jQuery实现两列等高并自适应高度
2016/12/22 Javascript
原生js编写2048小游戏
2017/03/17 Javascript
详解Vue 实例中的生命周期钩子
2017/03/21 Javascript
基于JavaScript实现的希尔排序算法分析
2017/04/14 Javascript
jQuery实现选中行变色效果(实例讲解)
2017/07/06 jQuery
Vue+Jwt+SpringBoot+Ldap完成登录认证的示例代码
2018/05/21 Javascript
js 下拉菜单点击旁边收起实现(踩坑记)
2019/09/29 Javascript
使用JS监听键盘按下事件(keydown event)
2019/11/07 Javascript
vue keep-alive列表页缓存 详情页返回上一页不刷新,定位到之前位置
2019/11/26 Javascript
详解阿里Node.js技术文档之process模块学习指南
2021/01/04 Javascript
Python中使用socket发送HTTP请求数据接收不完整问题解决方法
2015/02/04 Python
Python的Django框架可适配的各种数据库介绍
2015/07/15 Python
python使用str & repr转换字符串
2016/10/13 Python
python+selenium实现163邮箱自动登陆的方法
2017/12/31 Python
Django使用paginator插件实现翻页功能的实例
2018/10/24 Python
matlab灰度图像调整及imadjust函数的用法详解
2020/02/27 Python
aws 通过boto3 python脚本打pach的实现方法
2020/05/10 Python
化工工艺专业求职信
2013/09/22 职场文书
电子商务个人自荐信
2013/12/12 职场文书
普通大学毕业生自荐信范文
2014/02/23 职场文书
委托书范文
2014/04/02 职场文书
宣传普通话标语
2014/06/27 职场文书
求职意向书范本
2015/05/11 职场文书
机关干部纪律作风整顿心得体会
2016/01/23 职场文书
Python中文纠错的简单实现
2021/07/07 Python