Promise笔记

Promise学习(上):

资料: JavaScript Promise迷你书 

原著:azu / 翻译:liubin、kaku、honnkyou Version 1.4.1   /  http://liubin.org/promises-book/

概括笔记:

一、promise基本概念

1、Promise是抽象异步处理对象以及对其进行各种操作的组件。

2、Promise并不是从JavaScript中发祥的概念。Promise最初被提出是在E语言中, 它是基于并列/并行处理设计的一种编程语言。

3、Promise把类似的异步处理对象和处理规则进行规范化, 并按照采用统一的接口来编写,而采取规定方法之外的写法都会出错。

4、创建一个promise对象

从构造函数 Promise 来创建一个新建新promise对象作为接口。使用new来调用Promise的构造器来进行实例化promise对象。

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

  // 异步处理 

 // 处理结束后、调用resolve 或 reject

});

5、promise.then() 实例回调方法

promise.then() 实例promise对象在 resolve(成功) / reject(失败)时调用的回调函数。promise.then(onFulfilled, onRejected)。

resolve(成功)时onFulfilled 会被调;reject(失败)时onRejected 会被调用;onFulfilled、onRejected 两个都为可选参数。

promise.then 成功和失败时都可以使用。 如果只想对异常进行处理则可以采用 promise.then(undefined, onRejected) 方式,只指定reject时的回调函数。 不过promise除了then,还有catch方法,捕获失败的处理。 promise.catch(onRejected) 。promise.catch(onRejected)。

6、简单的例子

function asyncFunction() {

   return new Promise(function (resolve, reject) {

     setTimeout(function () {

        resolve(value);     

}, 16);    

}); 

}

asyncFunction().then(function (value) {

 console.log(value);  

  // => value

}).catch(function (error) {

  console.log(error); 

});

asyncFunction 函数返回promise对象, then 方法设置resolve后的回调函数, catch方法设置发生错误时的回调函数。在这种情况下 ,catch 的回调函数不会被执行(因为promise返回了resolve), 如果运行环境没有提供 setTimeout 函数,那么上面代码在执行中就会产生异常,在执行catch 中设置的回调函数。

 * 不使用catch 方法:

我们可以使用promise.then(onFulfilled, onRejected) 方法声明,只使用 then方法。

asyncFunction().then(function (value) {

    console.log(value); 

},function (error) {

  console.log(error); 

});

7、实例化的promise对象的状态。

"has-resolution" - Fulfilled :

resolve(成功)时。此时会调用 onFulfilled。

"has-rejection" - Rejected :

reject(失败)时。此时会调用 onRejected。

"unresolved" - Pending :

既不是resolve也不是reject的状态。也就是promise对象刚被创建后的初始化状态等。

其中 左侧为在ES6 Promises规范中定义的术语, 而右侧则是在Promises/A+中描述状态的术语。

8、promise对象的状态,从Pending转换为FulfilledRejected之后, 这个promise对象的状态就不会再发生任何变化。在.then 后执行的函数只会被调用一次。FulfilledRejected这两个中的任一状态都可以表示为Settled(不变的)。Settled 代表 resolve(成功) 或 reject(失败)。

* 关键点:当promise的对象状态发生变化时,用.then 来定义只会被调用一次的函数。

二、创建promise对象

1、创建promise对象的流程。

1.1、实例化promise对象:new Promise(fn) 返回一个promise对象

1.2、在fn 中指定异步等处理

处理结果错误的话,调用reject(Error对象)

处理结果正常的话,调用resolve(处理结果值)

2、为promise对象添加处理方法

promise对象的处理方法有两种:

promise对象被 resolve 时的处理(onFulfilled)

promise对象被 reject 时的处理(onRejected)

被resolve后的处理,可以在.then 方法中传入想要调用的函数。(如下面例子)getURL函数中的 resolve(req.responseText); 会将promise对象变为resolve(Fulfilled)状态, 同时使用其值调用 onFulfilled 函数。

在getURL 的处理中发生任何异常,或者被明确reject的情况下, 该异常原因(Error对象)会作为.catch方法的参数被调用。

例子:  用Promise来通过异步处理方式来获取XMLHttpRequest(XHR)的数据 :

声明一个getURL函数,返回一个promise实例包装XHR处理。

function getURL(URL) {  

  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){ // 被resolve后的处理,可以在.then方法中传入想要调用的函数。

  console.log(value);

 }).catch(function onRejected(error){ // 被reject后的处理,可以在.then 的第二个参数或者是在.catch方法中设置想要调用的函数。

  console.error(error);

 });

getURL 只有在通过XHR取得结果状态为200时才会调用 resolve -其他情况(取得失败)则调用 reject 方法。

XHR发生错误时 onerror 事件被触发,调用reject。发生错误时需要创建一个Error对象后再将具体的值传进去:reject(new Error(req.statusText)); 。

3、 Promise.resolve

new Promise() 方法的快捷方式:静态方法Promise.resolve(value)

例如:

基本的用法:

new Promise(function(resolve){  

  resolve(42);  //resolve(42); 会让promise对象立即进入确定(resolved)状态,并将 value值42 传递给后面then里所指定的 onFulfilled 函数

});

语法糖:

Promise.resolve(42);

Promise.resolve(value) 方法的返回值是一个promise对象,可以对其返回值进行 .then 调用。

Promise.resolve(42).then(function(value){   

 console.log(value);

 });

 Promise.resolve 方法的作用就是将传递给它的参数填充(Fulfilled)到promise对象后并返回这个promise对象。

3.1、Promise.resolve 方法另一个作用就是将thenable对象转换为promise对象

(thenable指的是一个具有 .then 方法的对象。例如jQuery.ajax())。thenable对象可以使用 Promise.resolve 来转换为一个promise对象。就能直接使用 then 或者 catch 等在ES6 Promises里定义的方法。

3.1.1、将thenable对象转换promise对象

var promise = Promise.resolve($.ajax('/json/comment.json'));// => promise对象

promise.then(function(value){  

 console.log(value);

 });

*thenable 对象我们一般用不到

4、 Promise.reject

Promise.reject(error)是和Promise.resolve(value)类似的静态方法,也是 new Promise() 方法的快捷方式。

常规写法是:

new Promise(function(resolve,reject){   

 reject(new Error("error"));

 });      //调用该promise对象通过then指定的 onRejected 函数,并将错误(Error)对象传递给 onRejected 函数。

语法糖:

Promise.reject(new Error("error")).catch(function(error){  

  console.error(error); 

});

5、promise异步操作

1、一般的使用情况下,接收回调函数的函数,根据具体的执行情况,可以选择是以同步还是异步的方式对回调函数进行调用。

异步回调函数同步调用 ?

NO!!!

1、绝对不能对异步回调函数(即使在数据已经就绪)进行同步调用。

2、如果对异步回调函数进行同步调用的话,处理顺序可能会与预期不符,可能带来意料之外的后果。

3、对异步回调函数进行同步调用,还可能导致栈溢出或异常处理错乱等问题。

4、如果想在将来某时刻调用异步回调函数的话,可以使用 setTimeout 等异步API。

例子:

这个函数会接收一个回调函数进行处理。

function onReady(fn) {   

 var readyState = document.readyState;

  if (readyState === 'interactive' || readyState === 'complete') {    

    fn();  

} else {

     window.addEventListener('DOMContentLoaded', fn); 

    }

 }

 onReady(function () {

    console.log('DOM fully loaded and parsed');

 }); 

console.log('==Starting==');

如果在调用onReady之前DOM已经载入的话

如果在调用onReady之前DOM还没有载入的话

对回调函数进行同步调用

通过注册 DOMContentLoaded 事件监听器来对回调函数进行异步调用

因此,如果这段代码在源文件中出现的位置不同,在控制台上打印的log消息顺序也会不同。为了解决这个问题,我们可以选择统一使用异步调用的方式。

例子2:

setTimeout 等异步API异步调用回调函数:

function onReady(fn) {  

  var readyState = document.readyState;

  if (readyState === 'interactive' || readyState === 'complete') {      

setTimeout(fn, 0);   

} else {

  window.addEventListener('DOMContentLoaded', fn);  

  } 

onReady(function () {

    console.log('DOM fully loaded and parsed');

 });

 console.log('==Starting==');

为了避免上述中同时使用同步、异步调用可能引起的混乱问题,Promise在规范上规定 Promise只能使用异步调用方式 。

例子三:

promise重写上述onReady函数:

function onReadyPromise() {  

  return new Promise(function (resolve, reject) {

  var readyState = document.readyState;

    if (readyState === 'interactive' || readyState === 'complete') {          

  resolve();   

} else {

       window.addEventListener('DOMContentLoaded', resolve);    

    }  

  }); 

onReadyPromise().then(function () {

 console.log('DOM fully loaded and parsed');

 }); 

console.log('==Starting==');

Promise能保证每次调用都是以异步方式进行。

6、Promise 方法链method chain

function taskA() {  

  console.log("Task A"); 

}

function taskB() {   

 console.log("Task B"); 

}

function onRejected(error) {  

  console.log("Catch Error: A or B", error); 

}

function finalTask() {   

 console.log("Final Task"); 

}

var promise = Promise.resolve();

 promise

.then(taskA)

.then(taskB)

.catch(onRejected) 

.then(finalTask);

执行流程:

6.1、promise chain 中如何传递参数

function doubleUp(value) {  

  return value * 2;

 }

function increment(value) {  

  return value + 1;

 }

function output(value) {  

  console.log(value);// => (1 + 1) * 2

}

var promise = Promise.resolve(1); 

promise

.then(increment)

.then(doubleUp)

.then(output)

.catch(function(error){ 

  // promise chain中出现异常的时候会被调用 

console.error(error);   

 });

入口函数是 Promise.resolve(1); 

执行流程:Promise.resolve(1); 传递 1 给 increment 函数;函数 increment 对接收的参数进行 +1 操作并返回结果,接着传给 doubleUp 函数;最后在函数 output 中打印结果。

* return的值会由 Promise.resolve(return的返回值); 进行相应的包装处理,最终 then 的结果返回一个新创建的promise对象。Promise#then 除了注册一个回调函数,还将回调函数的返回值进行变换,创建并返回一个promise对象。

特别注意的地方:then 返回返回新创建的promise对象。

例子:✘ then 的错误使用方法

function badAsyncCall() {  

  var promise = Promise.resolve();

  promise.then(function() {    

    // 任意处理 

 return newVar;

 });   

 return promise;

 }

正确方法:then 返回返回新创建的promise对象

function anAsyncCall() {  

  var promise = Promise.resolve();

  return promise.then(function() {   

     // 任意处理 

   return newVar;

 });

 }

这种函数的行为贯穿在Promise整体之中,接收一个promise对象为参数,并返回一个和接收参数不同的、新的promise对象。


7、 使用Promise#then同时处理多个异步请求

7.1、Promise.all

Promise.all 接收一个 promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用 .then 方法。

例子:

function getURL(URL) {   

 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 request = {

   comment: function getComment() {       

     return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse);     

   },      

  people: function getPeople() {     

       return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse);    

    }    

};

function main() {  

  return Promise.all([request.comment(), request.people()]);

}

// 运行示例

main().then(function (value) {

    console.log(value);

 }).catch(function(error){  

  console.log(error); 

});

// request.comment() 和 request.people() 会同时开始执行,而且每个promise的结果(resolve或reject时传递的参数值),和传递给Promise.all的promise数组的顺序一致。

7.2、Promise.all 在接收到的所有的对象promise都变为 FulFilled 或者 Rejected 状态之后才会继续进行后面的处理。

 与之相对的是 Promise.race 。只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。

暂时学习到前两章,东西太多,一时间消化不了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349