zoukankan      html  css  js  c++  java
  • ES6之Promise用法详解

    一 前言

    本文主要对ES6的Promise进行一些入门级的介绍。要想学习一个知识点,肯定是从三个方面出发,what、why、how。下面就跟着我一步步学习吧~

    二 什么是Promise

    首先是what。那么什么是Promise呢? 
    以下是MDN对Promise的定义

    The Promise object is used for asynchronous computations. A Promise represents a single asynchronous operation that hasn't completed yet, but is expected in the future.

    译文:Promise对象用于异步操作,它表示一个尚未完成且预计在未来完成的异步操作。

    那么什么是异步操作?在学习promise之前需要把这个概念搞明白,下面将抽离一章专门介绍。

    2.1 同步与异步

    我们知道,JavaScript的执行环境是「单线程」。 
    所谓单线程,是指JS引擎中负责解释和执行JavaScript代码的线程只有一个,也就是一次只能完成一项任务,这个任务执行完后才能执行下一个,它会「阻塞」其他任务。这个任务可称为主线程。 
    但实际上还有其他线程,如事件触发线程、ajax请求线程等。

    这也就引发了同步和异步的问题。

    2.1.1 同步

    同步模式,即上述所说的单线程模式,一次只能执行一个任务,函数调用后需等到函数执行结束,返回执行的结果,才能进行下一个任务。如果这个任务执行的时间较长,就会导致「线程阻塞」。

    1 /* 例2.1 */
    2 var x = true;
    3 while(x);
    4 console.log("don't carry out");    //不会执行

    上面的例子即同步模式,其中的while是一个死循环,它会阻塞进程,因此第三句console不会执行。 
    同步模式比较简单,也较容易编写。但问题也显而易见,如果请求的时间较长,而阻塞了后面代码的执行,体验是很不好的。因此对于一些耗时的操作,异步模式则是更好的选择。

    2.1.2 异步

    下面就来看看异步模式。 
    异步模式,即与同步模式相反,可以一起执行多个任务,函数调用后不会立即返回执行的结果,如果任务A需要等待,可先执行任务B,等到任务A结果返回后再继续回调。 
    最常见的异步模式就数定时器了,我们来看看以下的例子。

     1 /* 例2.2 */
     2 setTimeout(function() {
     3     console.log('taskA, asynchronous');
     4 }, 0);
     5 console.log('taskB, synchronize');
     6 //while(true);
     7 
     8 -------ouput-------
     9 taskB, synchronize
    10 taskA, asynchronous

    我们可以看到,定时器延时的时间明明为0,但taskA还是晚于taskB执行。这是为什么呢?由于定时器是异步的,异步任务会在当前脚本的所有同步任务执行完才会执行。如果同步代码中含有死循环,即将上例的注释去掉,那么这个异步任务就不会执行,因为同步任务阻塞了进程。

    2.1.3 回调函数

    提起异步,就不得不谈谈回调函数了。上例中,setTimeout里的function便是回调函数。可以简单理解为:(执行完)回(来)调(用)的函数。
    以下是WikiPedia对于callback的定义。

    In computer programming, a callback is a piece of executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at some convenient time.

    可以看出,回调函数是一段可执行的代码段,它以「参数」的形式传递给其他代码,在其合适的时间执行这段(回调函数)的代码。

    WikiPedia同时提到

    The invocation may be immediate as in a synchronous callback, or it might happen at a later time as in an asynchronous callback.

    也就是说,回调函数不仅可以用于异步调用,一般同步的场景也可以用回调。在同步调用下,回调函数一般是最后执行的。而异步调用下,可能一段时间后执行或不执行(未达到执行的条件)。

     1 /* 例2.3 */
     2 /******************同步回调******************/
     3 var fun1 = function(callback) {
     4     //do something
     5     console.log("before callback");
     6     (callback && typeof(callback) === 'function') && callback();
     7     console.log("after callback");
     8 }
     9 var fun2 = function(param) {
    10     //do something
    11     var start = new Date();
    12     while((new Date() - start) < 3000) { //delay 3s
    13     }
    14     console.log("I'm callback");
    15 }
    16 fun1(fun2);
    17 
    18 -------output--------
    19 before callback
    20 //after 3s
    21 I’m callback
    22 after callback

    由于是同步回调,会阻塞后面的代码,如果fun2是个死循环,后面的代码就不执行了。

    上一小节中setTimeout就是常见的异步回调,另外常见的异步回调即ajax请求。

     1 /* 例2.4 */
     2 /******************异步回调******************/
     3 function request(url, param, successFun, errorFun) {
     4     $.ajax({
     5         type: 'GET',
     6         url: url,
     7         param: param,
     8         async: true,    //默认为true,即异步请求;false为同步请求
     9         success: successFun,
    10         error: errorFun
    11     });
    12 }
    13 request('test.html', '', function(data) {
    14     //请求成功后的回调函数,通常是对请求回来的数据进行处理
    15     console.log('请求成功啦, 这是返回的数据:', data);
    16 },function(error) {
    17     console.log('sorry, 请求失败了, 这是失败信息:', error);
    18 });

    2.2 为什么使用Promise

    说完了以上基本概念,我们就可以继续学习Promise了。
    上面提到,Promise对象是用于异步操作的。既然我们可以使用异步回调来进行异步操作,为什么还要引入一个Promise新概念,还要花时间学习它呢?不要着急,下面就来谈谈Promise的过人之处。
    我们先看看下面的demo,利用Promise改写例2.4的异步回调。

     1 /* 例2.5 */
     2 function sendRequest(url, param) {
     3     return new Promise(function (resolve, reject) {
     4         request(url, param, resolve, reject);
     5     });
     6 }
     7 
     8 sendRequest('test.html', '').then(function(data) {
     9     //异步操作成功后的回调
    10     console.log('请求成功啦, 这是返回的数据:', data);
    11 }, function(error) {
    12     //异步操作失败后的回调
    13     console.log('sorry, 请求失败了, 这是失败信息:', error);
    14 });

    这么一看,并没有什么区别,还比上面的异步回调复杂,得先新建Promise再定义其回调。其实,Promise的真正强大之处在于它的多重链式调用,可以避免层层嵌套回调。如果我们在第一次ajax请求后,还要用它返回的结果再次请求呢?

     1 /* 例2.6 */
     2 request('test1.html', '', function(data1) {
     3     console.log('第一次请求成功, 这是返回的数据:', data1);
     4     request('test2.html', data1, function (data2) {
     5         console.log('第二次请求成功, 这是返回的数据:', data2);
     6         request('test3.html', data2, function (data3) {
     7             console.log('第三次请求成功, 这是返回的数据:', data3);
     8             //request... 继续请求
     9         }, function(error3) {
    10             console.log('第三次请求失败, 这是失败信息:', error3);
    11         });
    12     }, function(error2) {
    13         console.log('第二次请求失败, 这是失败信息:', error2);
    14     });
    15 }, function(error1) {
    16     console.log('第一次请求失败, 这是失败信息:', error1);
    17 });

    以上出现了多层回调嵌套,有种晕头转向的感觉。这也就是我们常说的厄运回调金字塔(Pyramid of Doom),编程体验十分不好。而使用Promise,我们就可以利用then进行「链式回调」,将异步操作以同步操作的流程表示出来。

     1 /* 例2.7 */
     2 sendRequest('test1.html', '').then(function(data1) {
     3     console.log('第一次请求成功, 这是返回的数据:', data1);
     4 }).then(function(data2) {
     5     console.log('第二次请求成功, 这是返回的数据:', data2);
     6 }).then(function(data3) {
     7     console.log('第三次请求成功, 这是返回的数据:', data3);
     8 }).catch(function(error) {
     9     //用catch捕捉前面的错误
    10     console.log('sorry, 请求失败了, 这是失败信息:', error);
    11 });

    是不是明显清晰很多?孰优孰略也无需多说了吧~下面就让我们真正进入Promise的学习。

    三 Promise的基本用法

    3.1 基本用法

    上一小节我们认识了promise长什么样,但对它用到的resolverejectthencatch想必还不理解。下面我们一步步学习。

    Promise对象代表一个未完成、但预计将来会完成的操作。
    它有以下三种状态:

    • pending:初始值,不是fulfilled,也不是rejected
    • fulfilled:代表操作成功
    • rejected:代表操作失败

    Promise有两种状态改变的方式,既可以从pending转变为fulfilled,也可以从pending转变为rejected。一旦状态改变,就「凝固」了,会一直保持这个状态,不会再发生变化。当状态发生变化,promise.then绑定的函数就会被调用。
    注意:Promise一旦新建就会「立即执行」,无法取消。这也是它的缺点之一。
    下面就通过例子进一步讲解。

     1 /* 例3.1 */
     2 //构建Promise
     3 var promise = new Promise(function (resolve, reject) {
     4     if (/* 异步操作成功 */) {
     5         resolve(data);
     6     } else {
     7         /* 异步操作失败 */
     8         reject(error);
     9     }
    10 });

    类似构建对象,我们使用new来构建一个PromisePromise接受一个「函数」作为参数,该函数的两个参数分别是resolvereject。这两个函数就是就是「回调函数」,由JavaScript引擎提供。

    resolve函数的作用:在异步操作成功时调用,并将异步操作的结果,作为参数传递出去; 
    reject函数的作用:在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

    Promise实例生成以后,可以用then方法指定resolved状态和reject状态的回调函数。

    1 /* 接例3.1 */
    2 promise.then(onFulfilled, onRejected);
    3 
    4 promise.then(function(data) {
    5   // do something when success
    6 }, function(error) {
    7   // do something when failure
    8 });

    then方法会返回一个Promise。它有两个参数,分别为Promise从pending变为fulfilledrejected时的回调函数(第二个参数非必选)。这两个函数都接受Promise对象传出的值作为参数
    简单来说,then就是定义resolvereject函数的,其resolve参数相当于:

    1 function resolveFun(data) {
    2     //data为promise传出的值
    3 }

    而新建Promise中的'resolve(data)',则相当于执行resolveFun函数。
    Promise新建后就会立即执行。而then方法中指定的回调函数,将在当前脚本所有同步任务执行完才会执行。如下例:

     1 /* 例3.2 */
     2 var promise = new Promise(function(resolve, reject) {
     3   console.log('before resolved');
     4   resolve();
     5   console.log('after resolved');
     6 });
     7 
     8 promise.then(function() {
     9   console.log('resolved');
    10 });
    11 
    12 console.log('outer');
    13 
    14 -------output-------
    15 before resolved
    16 after resolved
    17 outer
    18 resolved

    由于resolve指定的是异步操作成功后的回调函数,它需要等所有同步代码执行后才会执行,因此最后打印'resolved',这个和例2.2是一样的道理。

    3.2 基本API

    .then()

    语法:Promise.prototype.then(onFulfilled, onRejected)
    

    对promise添加onFulfilledonRejected回调,并返回的是一个新的Promise实例(不是原来那个Promise实例),且返回值将作为参数传入这个新Promise的resolve函数。

    因此,我们可以使用链式写法,如上文的例2.7。由于前一个回调函数,返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。

    .catch()

    语法:Promise.prototype.catch(onRejected)
    

    该方法是.then(undefined, onRejected)的别名,用于指定发生错误时的回调函数。

     1 /* 例3.3 */
     2 promise.then(function(data) {
     3     console.log('success');
     4 }).catch(function(error) {
     5     console.log('error', error);
     6 });
     7 
     8 /*******等同于*******/
     9 promise.then(function(data) {
    10     console.log('success');
    11 }).then(undefined, function(error) {
    12     console.log('error', error);
    13 });
     1 /* 例3.4 */
     2 var promise = new Promise(function (resolve, reject) {
     3     throw new Error('test');
     4 });
     5 /*******等同于*******/
     6 var promise = new Promise(function (resolve, reject) {
     7     reject(new Error('test'));
     8 });
     9 
    10 //用catch捕获
    11 promise.catch(function (error) {
    12     console.log(error);
    13 });
    14 -------output-------
    15 Error: test

    从上例可以看出,reject方法的作用,等同于抛错。

    promise对象的错误,会一直向后传递,直到被捕获。即错误总会被下一个catch所捕获。then方法指定的回调函数,若抛出错误,也会被下一个catch捕获。catch中也能抛错,则需要后面的catch来捕获。

    1 /* 例3.5 */
    2 sendRequest('test.html').then(function(data1) {
    3     //do something
    4 }).then(function (data2) {
    5     //do something
    6 }).catch(function (error) {
    7     //处理前面三个Promise产生的错误
    8 });

    上文提到过,promise状态一旦改变就会凝固,不会再改变。因此promise一旦fulfilled了,再抛错,也不会变为rejected,就不会被catch了。

    1 /* 例3.6 */
    2 var promise = new Promise(function(resolve, reject) {
    3   resolve();
    4   throw 'error';
    5 });
    6 
    7 promise.catch(function(e) {
    8    console.log(e);      //This is never called
    9 });

    如果没有使用catch方法指定处理错误的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应(Chrome会抛错),这是Promise的另一个缺点。

    1 /* 例3.7 */
    2 var promise = new Promise(function (resolve, reject) {
    3     resolve(x);
    4 });
    5 promise.then(function (data) {
    6     console.log(data);
    7 });

     

    如图所示,只有Chrome会抛错,且promise状态变为rejected,Firefox和Safari中错误不会被捕获,也不会传递到外层代码,最后没有任何输出,promise状态也变为rejected

    .all()

    语法:Promise.all(iterable)
    

    该方法用于将多个Promise实例,包装成一个新的Promise实例。

    var p = Promise.all([p1, p2, p3]);

    Promise.all方法接受一个数组(或具有Iterator接口)作参数,数组中的对象(p1、p2、p3)均为promise实例(如果不是一个promise,该项会被用Promise.resolve转换为一个promise)。它的状态由这三个promise实例决定。

    • 当p1, p2, p3状态都变为fulfilled,p的状态才会变为fulfilled,并将三个promise返回的结果,按参数的顺序(而不是 resolved的顺序)存入数组,传给p的回调函数,如例3.8。
    • 当p1, p2, p3其中之一状态变为rejected,p的状态也会变为rejected,并把第一个被reject的promise的返回值,传给p的回调函数,如例3.9。
     1 /* 例3.8 */
     2 var p1 = new Promise(function (resolve, reject) {
     3     setTimeout(resolve, 3000, "first");
     4 });
     5 var p2 = new Promise(function (resolve, reject) {
     6     resolve('second');
     7 });
     8 var p3 = new Promise((resolve, reject) => {
     9   setTimeout(resolve, 1000, "third");
    10 }); 
    11 
    12 Promise.all([p1, p2, p3]).then(function(values) { 
    13   console.log(values); 
    14 });
    15 
    16 -------output-------
    17 //约 3s 后
    18 ["first", "second", "third"] 
     1 /* 例3.9 */
     2 var p1 = new Promise((resolve, reject) => { 
     3   setTimeout(resolve, 1000, "one"); 
     4 }); 
     5 var p2 = new Promise((resolve, reject) => { 
     6   setTimeout(reject, 2000, "two"); 
     7 });
     8 var p3 = new Promise((resolve, reject) => {
     9   reject("three");
    10 });
    11 
    12 Promise.all([p1, p2, p3]).then(function (value) {
    13     console.log('resolve', value);
    14 }, function (error) {
    15     console.log('reject', error);    // => reject three
    16 });
    17 
    18 -------output-------
    19 reject three

    这多个 promise 是同时开始、并行执行的,而不是顺序执行。从下面例子可以看出。如果一个个执行,那至少需要 1+32+64+128

     1 /* 例3.10 */
     2 function timerPromisefy(delay) {
     3     return new Promise(function (resolve) {
     4         setTimeout(function () {
     5             resolve(delay);
     6         }, delay);
     7     });
     8 }
     9 var startDate = Date.now();
    10 
    11 Promise.all([
    12     timerPromisefy(1),
    13     timerPromisefy(32),
    14     timerPromisefy(64),
    15     timerPromisefy(128)
    16 ]).then(function (values) {
    17     console.log(Date.now() - startDate + 'ms');
    18     console.log(values);
    19 });
    20 -------output-------
    21 133ms       //不一定,但大于128ms
    22 [1,32,64,128]

    .race()

    语法:Promise.race(iterable)
    

    该方法同样是将多个Promise实例,包装成一个新的Promise实例。

    var p = Promise.race([p1, p2, p3]);

    Promise.race方法同样接受一个数组(或具有Iterator接口)作参数。当p1, p2, p3中有一个实例的状态发生改变(变为fulfilledrejected),p的状态就跟着改变。并把第一个改变状态的promise的返回值,传给p的回调函数。

     1 /* 例3.11 */
     2 var p1 = new Promise(function(resolve, reject) { 
     3     setTimeout(reject, 500, "one"); 
     4 });
     5 var p2 = new Promise(function(resolve, reject) { 
     6     setTimeout(resolve, 100, "two"); 
     7 });
     8 
     9 Promise.race([p1, p2]).then(function(value) {
    10     console.log('resolve', value); 
    11 }, function(error) {
    12     //not called
    13     console.log('reject', error); 
    14 });
    15 -------output-------
    16 resolve two
    17 
    18 var p3 = new Promise(function(resolve, reject) { 
    19     setTimeout(resolve, 500, "three");
    20 });
    21 var p4 = new Promise(function(resolve, reject) { 
    22     setTimeout(reject, 100, "four"); 
    23 });
    24 
    25 Promise.race([p3, p4]).then(function(value) {
    26     //not called
    27     console.log('resolve', value);              
    28 }, function(error) {
    29     console.log('reject', error); 
    30 });
    31 -------output-------
    32 reject four

    在第一个promise对象变为resolve后,并不会取消其他promise对象的执行,如下例

     1 /* 例3.12 */
     2 var fastPromise = new Promise(function (resolve) {
     3     setTimeout(function () {
     4         console.log('fastPromise');
     5         resolve('resolve fastPromise');
     6     }, 100);
     7 });
     8 var slowPromise = new Promise(function (resolve) {
     9     setTimeout(function () {
    10         console.log('slowPromise');
    11         resolve('resolve slowPromise');
    12     }, 1000);
    13 });
    14 // 第一个promise变为resolve后程序停止
    15 Promise.race([fastPromise, slowPromise]).then(function (value) {
    16     console.log(value);    // => resolve fastPromise
    17 });
    18 -------output-------
    19 fastPromise
    20 resolve fastPromise
    21 slowPromise     //仍会执行

    .resolve()

    语法:

     
    Promise.resolve(value);
    Promise.resolve(promise);
    Promise.resolve(thenable);

    它可以看做new Promise()的快捷方式。

    1 Promise.resolve('Success');
    2 
    3 /*******等同于*******/
    4 new Promise(function (resolve) {
    5     resolve('Success');
    6 });

    这段代码会让这个Promise对象立即进入resolved状态,并将结果success传递给then指定的onFulfilled回调函数。由于Promise.resolve()也是返回Promise对象,因此可以用.then()处理其返回值。

    /* 例3.13 */
    Promise.resolve('success').then(function (value) {
        console.log(value);
    });
    -------output-------
    Success
    /* 例3.14 */
    //Resolving an array
    Promise.resolve([1,2,3]).then(function(value) {
      console.log(value[0]);    // => 1
    });
    
    //Resolving a Promise
    var p1 = Promise.resolve('this is p1');
    var p2 = Promise.resolve(p1);
    p2.then(function (value) {
        console.log(value);     // => this is p1
    });

    Promise.resolve()的另一个作用就是将thenable对象(即带有then方法的对象)转换为promise对象。

     1 /* 例3.15 */
     2 var p1 = Promise.resolve({ 
     3     then: function (resolve, reject) { 
     4         resolve("this is an thenable object!");
     5     }
     6 });
     7 console.log(p1 instanceof Promise);     // => true
     8 
     9 p1.then(function(value) {
    10     console.log(value);     // => this is an thenable object!
    11   }, function(e) {
    12     //not called
    13 });

    再看下面两个例子,无论是在什么时候抛异常,只要promise状态变成resolvedrejected,状态不会再改变,这和新建promise是一样的。

     1 /* 例3.16 */
     2 //在回调函数前抛异常
     3 var p1 = { 
     4     then: function(resolve) {
     5       throw new Error("error");
     6       resolve("Resolved");
     7     }
     8 };
     9 
    10 var p2 = Promise.resolve(p1);
    11 p2.then(function(value) {
    12     //not called
    13 }, function(error) {
    14     console.log(error);       // => Error: error
    15 });
    16 
    17 //在回调函数后抛异常
    18 var p3 = { 
    19     then: function(resolve) {
    20         resolve("Resolved");
    21         throw new Error("error");
    22     }
    23 };
    24 
    25 var p4 = Promise.resolve(p3);
    26 p4.then(function(value) {
    27     console.log(value);     // => Resolved
    28 }, function(error) {
    29     //not called
    30 });

    .reject()

    语法:Promise.reject(reason)
    

    这个方法和上述的Promise.resolve()类似,它也是new Promise()的快捷方式。

    1 Promise.reject(new Error('error'));
    2 
    3 /*******等同于*******/
    4 new Promise(function (resolve, reject) {
    5     reject(new Error('error'));
    6 });

    这段代码会让这个Promise对象立即进入rejected状态,并将错误对象传递给then指定的onRejected回调函数。

    四 Promise常见问题

    经过上一章的学习,相信大家已经学会使用Promise
    总结一下创建promise的流程:

    1. 使用new Promise(fn)或者它的快捷方式Promise.resolve()Promise.reject(),返回一个promise对象
    2. fn中指定异步的处理
      处理结果正常,调用resolve
      处理结果错误,调用reject

    如果使用ES6的箭头函数,将会使写法更加简单清晰。

    这一章节,将会用例子的形式,以说明promise使用过程中的注意点及容易犯的错误。

    情景1:reject 和 catch 的区别

    • promise.then(onFulfilled, onRejected)
      onFulfilled中发生异常的话,在onRejected中是捕获不到这个异常的。
    • promise.then(onFulfilled).catch(onRejected)
      .then中产生的异常能在.catch中捕获

    一般情况,还是建议使用第二种,因为能捕获之前的所有异常。当然了,第二种的.catch()也可以使用.then()表示,它们本质上是没有区别的,.catch === .then(null, onRejected)


    情景2:如果在then中抛错,而没有对错误进行处理(即catch),那么会一直保持reject状态,直到catch了错误

     1 /* 例4.1 */
     2 function taskA() {
     3     console.log(x);
     4     console.log("Task A");
     5 }
     6 function taskB() {
     7     console.log("Task B");
     8 }
     9 function onRejected(error) {
    10     console.log("Catch Error: A or B", error);
    11 }
    12 function finalTask() {
    13     console.log("Final Task");
    14 }
    15 var promise = Promise.resolve();
    16 promise
    17     .then(taskA)
    18     .then(taskB)
    19     .catch(onRejected)
    20     .then(finalTask);
    21     
    22 -------output-------
    23 Catch Error: A or B,ReferenceError: x is not defined
    24 Final Task

    根据例4.1的输出结果及流程图,可以看出,A抛错时,会按照 taskA → onRejected → finalTask这个流程来处理。A抛错后,若没有对它进行处理,如例3.7,状态就会维持rejected,taskB不会执行,直到catch了错误。

     1 /* 例4.2 */
     2 function taskA() {
     3     console.log(x);
     4     console.log("Task A");
     5 }
     6 function taskB() {
     7     console.log("Task B");
     8 }
     9 function onRejectedA(error) {
    10     console.log("Catch Error: A", error);
    11 }
    12 function onRejectedB(error) {
    13     console.log("Catch Error: B", error);
    14 }
    15 function finalTask() {
    16     console.log("Final Task");
    17 }
    18 var promise = Promise.resolve();
    19 promise
    20     .then(taskA)
    21     .catch(onRejectedA)
    22     .then(taskB)
    23     .catch(onRejectedB)
    24     .then(finalTask);
    25     
    26 -------output-------
    27 Catch Error: A ReferenceError: x is not defined
    28 Task B
    29 Final Task

    将例4.2与4.1对比,在taskA后多了对A的处理,因此,A抛错时,会按照A会按照 taskA → onRejectedA → taskB → finalTask这个流程来处理,此时taskB是正常执行的。


    情景3:每次调用then都会返回一个新创建的promise对象,而then内部只是返回的数据

     1 /* 例4.3 */
     2 //方法1:对同一个promise对象同时调用 then 方法
     3 var p1 = new Promise(function (resolve) {
     4     resolve(100);
     5 });
     6 p1.then(function (value) {
     7     return value * 2;
     8 });
     9 p1.then(function (value) {
    10     return value * 2;
    11 });
    12 p1.then(function (value) {
    13     console.log("finally: " + value);
    14 });
    15 -------output-------
    16 finally: 100
    17 
    18 //方法2:对 then 进行 promise chain 方式进行调用
    19 var p2 = new Promise(function (resolve) {
    20     resolve(100);
    21 });
    22 p2.then(function (value) {
    23     return value * 2;
    24 }).then(function (value) {
    25     return value * 2;
    26 }).then(function (value) {
    27     console.log("finally: " + value);
    28 });
    29 -------output-------
    30 finally: 400

    第一种方法中,then的调用几乎是同时开始执行的,且传给每个then的value都是100,这种方法应当避免。方法二才是正确的链式调用。
    因此容易出现下面的错误写法:

     1 /* 例4.4 */
     2 function badAsyncCall(data) {
     3     var promise = Promise.resolve(data);
     4     promise.then(function(value) {
     5         //do something
     6         return value + 1;
     7     });
     8     return promise;
     9 }
    10 badAsyncCall(10).then(function(value) {
    11    console.log(value);          //想要得到11,实际输出10
    12 });
    13 -------output-------
    14 10

    正确的写法应该是:

     1 /* 改写例4.4 */
     2 function goodAsyncCall(data) {
     3     var promise = Promise.resolve(data);
     4     return promise.then(function(value) {
     5         //do something
     6         return value + 1;
     7     });
     8 }
     9 goodAsyncCall(10).then(function(value) {
    10    console.log(value);
    11 });
    12 -------output-------
    13 11

    情景4:在异步回调中抛错,不会被catch

     1 // Errors thrown inside asynchronous functions will act like uncaught errors
     2 var promise = new Promise(function(resolve, reject) {
     3   setTimeout(function() {
     4     throw 'Uncaught Exception!';
     5   }, 1000);
     6 });
     7 
     8 promise.catch(function(e) {
     9   console.log(e);       //This is never called
    10 });

    情景5: promise状态变为resovereject,就凝固了,不会再改变

    console.log(1);
    new Promise(function (resolve, reject){
        reject();
        setTimeout(function (){
            resolve();            //not called
        }, 0);
    }).then(function(){
        console.log(2);
    }, function(){
        console.log(3);
    });
    console.log(4);
    
    -------output-------
    1
    4
    3

    五 结语

    关于promise就先介绍到这边了,比较基础,有不足的地方欢迎指出,有更好的也欢迎补充~

  • 相关阅读:
    第一篇:理论篇
    day 3:注释,缩进
    HTML的报告
    SAP DIALOG屏幕新增搜索帮助
    财务凭证科目替代(未写完)
    表维护生成器本地转请求包
    web安全之SQL注入
    Ubuntu16.04 下安装Sublime Text 3
    ubuntu16.04 下安装配置python3.6
    Ubuntu 16.04 下安装 PyCharm
  • 原文地址:https://www.cnblogs.com/le220/p/10381920.html
Copyright © 2011-2022 走看看