深入理解Promise

编后吐槽:写的快花眼,很详细,耐心看必受益匪浅

JavaScript的执行环境是「单线程」的。所谓单线程,是指JS引擎中负责解释和执行JavaScript代码的线程只有一个,也就是一次只能完成一项任务,这个任务执行完后才能执行下一个,它会「阻塞」其他任务。这个任务可称为主线程。但实际上还有其他线程,如事件触发线程、ajax请求线程等。因为javascript的单线程原理,使得网络操作,浏览器事件,都必须是异步执行的。

* 同步与异步

  • 同步:同步模式,即上述所说的单线程模式,一次只能执行一个任务,函数调用后需等到函数执行结束,返回执行的结果,才能进行下一个任务。如果这个任务执行的时间较长,就会导致「线程阻塞」。
var x = true;
while(x);
console.log("don‘t’ carry out");
// javascript 单线程原理导致同步执行,第三步不被执行
  • 异步:可以一起执行多个任务,函数调用后不会立即返回执行的结果,如果任务A需要等待,可先执行任务B,等到任务A结果返回后再继续回调。
// 常见异步,定时器的使用
setTimeout(function() {
  console.log('taskA, run as asynchronous');
}, 0)
console.log('taskB, run as synchronize');
//while(true);

// taskB, run as synchronize
// taskA, run as asynchronous

 即使延时时长为0,taskA依旧晚于taskB;因为定时器是异步的,异步任务会在当前脚本所有同步任务完成之后才被执行。,如果同步任务中含有阻塞任务,即放开代码中的while(true),那么taskA将不会被执行。

* 什么是Promise

  古人云:“君子一诺千金”,这种“承诺某个状态将来会执行,并且该状态不会被改变”的对象在JavaScript中称为Promise对象,他是一个构造函数,能将异步的操作以同步的形式表达出来,避免了嵌套地狱(层层嵌套)的发生。它供了统一的API,使得控制异步更加容易

* 回调函数

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

// 同步回调
var func1 = function(cb) {
  // ..do something
  console.log("before callback");
  (cb && typeof(cb) === 'function') && cb();
  console.log("after callback");
}
var func2 = function(param) {
  // ..do something
  var start = new Date();
  if(( new Date() - start) < 3000 ){ }  // 同步实现延时函数
  console.log("I am callback");
}

func1(func2);

------output------- 
// before callback
(after 3s...)
// I am callback
// after callback

 由于是同步回调,会阻塞后面的代码,如果func2是个死循环,后面的代码就不执行了。为解决这个问题,我们使用异步回调,除了常见的setTimeoutajax也是应用方式之一

// 异步回调
function request(url, param, successFun, errorFunc) {
  $.ajax({
    type: 'GET',
    url: url,
    param: param,
    async: true,  // 默认为true,如果设置为false则变成同步请求
    success: successFunc,
    error: errorFunc
  });
}
request('test.html', '', function(data) {
  // 第三个参数为返回成功时执行的函数
  console.log('cbData: ', data);
}, function(data) {
  // 第四个参数为失败时的函数
  console.log('error: ', error);
});

* 为什么要用Promise

既然已经可以实现异步回调,那我们为什么还要用Promise呢?
javascript代码

function callback () {
  console.log("Done");
}

console.log("before setTimeout()")
setTimeout(callback, 1000);
console.log("after setTimeout()"); 

控制台输出

before setTimeout()
after setTimeout()
(等待一秒后...)
Done

说明: setTimeout()延时函数时javascript实现异步执行的手段之一,该例中它将callback放到等待队列,并且开始为延时量计时,当到达延时量主线程中的事件还没执行完成,那等待队列中的事件继续保持等待,直到主线程的执行完成才被加入到线程中执行。所以代码中说的”等待一秒后”其实并不准确。

 可见,异步操作会在将来的某个时间点触发一个函数调用。
 如果我们有这样一个需求:下一个请求或函数必须要有上一步返回的数据才能执行,如下:

request('test1.html', '', function(data1) {
    console.log('第一次请求成功, 这是返回的数据:', data1);
    request('test2.html', data1, function (data2) {
        console.log('第二次请求成功, 这是返回的数据:', data2);
        request('test3.html', data2, function (data3) {
            console.log('第三次请求成功, 这是返回的数据:', data3);
            //request... 继续请求
        }, function(error3) {
            console.log('第三次请求失败, 这是失败信息:', error3);
        });
    }, function(error2) {
        console.log('第二次请求失败, 这是失败信息:', error2);
    });
}, function(error1) {
    console.log('第一次请求失败, 这是失败信息:', error1);
});

 以上出现了多层回调嵌套,有种晕头转向的感觉。这也就是我们常说的厄运回调金字塔(Pyramid of Doom),编程体验十分不好
  不仅如此,这种代码结构,可读性可维护性太差,代码复用率也很低,我们希望能将数据请求和数据处理区分开来,Promise就可以利用then进行「链式回调」,将异步操作以同步操作的流程表示出来。

sendRequest('test1.html', '').then(function(data1) {
    console.log('第一次请求成功, 这是返回的数据:', data1);
}).then(function(data2) {
    console.log('第二次请求成功, 这是返回的数据:', data2);
}).then(function(data3) {
    console.log('第三次请求成功, 这是返回的数据:', data3);
}).catch(function(error) {
    //用catch捕捉前面的错误
    console.log('sorry, 请求失败了, 这是失败信息:', error);
});

 Promise不仅能以同步的方式表示异步操作,还能catch到异常,这是ajax无法实现的

* Promise in JS

1. 基本结构

结构示意图

ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。
javascript代码

var pm = new Promise(function(resolve, reject) {
  // ..some code
  if( /*异步操作结果*/) {
    resolve(value);
  } else {
    reject(error);
  }
})

pm.then(function(){
  // handle resolveFunc
},function() {
  // handle rejectFunc
})

这就是它的基本模型,为了继续往下学习,我们需要补充一下Promise的一些基础知识。

2. Promise 三种状态

Promise链式调用用到resolverejectthencatch,他有以下三种状态

  • pending - 进行中,或者等待中,表示还没有得到结果
  • fulfilled - 已成功,在异步操作成功时调用,并将结果作为参数传递出去。
  • rejected - 已失败。在异步操作失败时调用,并将报出的错误作为参数传递出去。

只有异步操作的结果可以决定当前是哪种状态,其他任何操作都无法改变这个状态,这也是Promise名字的由来。所谓的“君子一言,驷马难追”,承诺将来某个状态,且该状态不会被其他因素改变。

注释: 从基本用法的例子中我们看到Promise构造函数的参数是resolvereject,并不是三种状态中的fulfilledrejected,原因就是:resolved表示的是已结束(已定型),它包含fullfilledrejected两种状态,但使用中,我们默认的将resolved当做fulfilled(成功)使用。

3. Promise 对象的特点

(1)对象的状态不受外界因素影响。以上已经说过。
(2)一旦结果状态生成,就不会在改变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种状态发生了,状态就凝固不变,并一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这和事件监听有很大的区别,事件监听是实时的,如果错过了监听时机,就得不到要监听的结果了

promise也有缺点
  • 无法取消Promise,一旦新建它就会立即执行,无法中途取消
  • 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部(所以健壮的代码要catch错误)
  • 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

* 基本API

1 .then()

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

这是Promise最常用也是最重要的一个API,他定义了Promise的两个回调函数,并返回一个新的Promise实例,且返回值传入这个行Promise的resolve函数中。
因此,我们可以使用链式写法,如为什么要用Promise中的最后一例,返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。

2. .catch() - 抛出异常

语法: Promise.prototype.catch( onRejected )

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

var promise = new Promise(function(resolve, reject)){
  // some code
}

promise.then(function(data)  {
  console.log('success');
}, function(error) {
  conosle.log('error', error)
})

/*---等价于---*/
promise.then(function(data){
  console.log('success');
}).catch(function(error) {
  consol;e.log('error', error)
})

再看一例

var promise = new Promise(function(resoleve, reject) {
  throw new Error('test');
});
// 等同于
var promise = new Promise(resovle, reject) {
  reject(new Error('test'));
}

// 以上的reject我们用catch来捕获
promise.catch(function (error) {
  console.log(error);
});
---output---
Error: test

 从上例可以看出,reject方法的作用,等同于抛错,这是Promise另一大优势

 关于Promise抛错有几下几个需要注意的地方

  1. Promise对象的错误,会一直向后传递,知道被捕获,即错误总会被下一个catch所捕获。then方法指定的回调函数,若抛出错误,也会被下一个catch捕获。catch中也能抛错,则需要后面的catch来捕获。
 sendRequest('test.html').then(function(data1) {
    //do something
}).then(function (data2) {
    //do something
}).catch(function (error) {
    //处理前面三个Promise产生的错误
});
  1. promise状态一旦改变就会凝固,不会再改变。因此promise一旦fulfilled了,再抛错,也不会变为rejected,就不会被catch了。
var promise = new Promise(function(resolve, reject) {
  resolve();
  throw 'error';
});

promise.catch(function(e) {
   console.log(e);      //This is never called
});
  1. 如果没有使用catch方法指定处理错误的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应(Chrome会抛错),这是Promise的另一个缺点。
var promise = new Promise(function(resolve, reject) {
  resolve();  // 状态已经被返回为resolve
  throw 'error';
});

promise.catch(function(e) {
   console.log(e);      //This is never called
});

抛错实例:

var p = new Promise(function(resolve, reject) {
  resolve(x);
});
p.then(function(data){
  console.log(data);
});

Chrome上的表现;


状态为reject且控制台报错

  据说只有chrome会报错,其他浏览器的错误不会被捕获,也不会传递到外层代码,最后没有任何输出,promise的状态也变为rejected

3 .all() - Promise中的“逻辑与”

  全部执行结束且状态均为resolve才为resolve,有一个执行错误,则为reject

语法: promise.all( iterable )

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

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

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

  1. 当p1, p2, p3状态都变为fulfilled,p的状态才会变为fulfilled,并将三个promise返回的结果,按参数的顺序(而不是 resolved的顺序)存入数组,传给p的回调函数
var p1 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 3000, "first");
});
var p2 = new Promise(function(resolve, reject) {
  resolve("second");
});
var p3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 1000, "third");
});

Promise.all([p1, p2, p3]).then(function(values) {
  console.log(values);
});
 
// ----output----
// 约3秒后
// ["first", "second", "third"]
  1. 当p1, p2, p3其中之一状态变为rejected,p的状态也会变为rejected,并把第一个被reject的promise的返回值,立即触发并传给p的回调函数
// 将上例中的p2适当修改如下
var p2 = new Promise(function(resolve, reject) {
  resolve(x);
});

这时,p2会抛出错误,立即传给Promise.all(),结束执行。

  1. 这多个 promise 是同时开始、并行执行的,而不是顺序执行

4 .race() - 竞速执行

  需要注意的是,它并不是Promise中的“逻辑或”,而是将先结束的传值给 then,最先执行完成的是resolveresolve,为rejectreject

语法: Promise.race( iterable )

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

var p = Promise.race([p1, p2, p3]);
  1. Promise.race方法同样接受一个数组(或具有Iterator接口)作参数。当p1, p2, p3中有一个实例的状态发生改变(变为fulfilled或rejected),p的状态就跟着改变。并把第一个改变状态的promise的返回值,传给p的回调函数。
    执行resolve
var p1 = new Promise(function(resolve, reject) { 
    setTimeout(reject, 500, "one"); 
});
var p2 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 100, "two"); 
});

Promise.race([p1, p2]).then(function(value) {
    console.log('resolve', value); 
}, function(error) {
    //not called
    console.log('reject', error); 
});
-------output-------
resolve two

执行reject

var p3 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 500, "three");
});
var p4 = new Promise(function(resolve, reject) { 
    setTimeout(reject, 100, "four"); 
});

Promise.race([p3, p4]).then(function(value) {
    //not called
    console.log('resolve', value);              
}, function(error) {
    console.log('reject', error); 
});
-------output-------
reject four
  1. 在第一个promise对象变为resolve后,并不会取消其他promise对象的执行,如下例
var fastPromise = new Promise(function (resolve) {
    setTimeout(function () {
        console.log('fastPromise');
        resolve('resolve fastPromise');
    }, 100);
});
var slowPromise = new Promise(function (resolve) {
    setTimeout(function () {
        console.log('slowPromise');
        resolve('resolve slowPromise');
    }, 1000);
});
// 第一个promise变为resolve后程序停止
Promise.race([fastPromise, slowPromise]).then(function (value) {
    console.log(value);    // => resolve fastPromise
});
-------output-------
fastPromise
resolve fastPromise
slowPromise     //仍会执行

5 .resolve() - 立即执行Promise-resolve

语法: 1. Promise.resolve(value); 2. Promise.resolve(promise); 3. Promise.resolve(thenable);

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

new Promise(function (resolve) {
    resolve('Success');
});
//----等同于----
Promise.resolve('Success');
  1. 这段代码会让这个Promise对象立即进入resolved状态,并将结果success传递给then指定的onFulfilled回调函数。由于Promise.resolve()也是返回Promise对象,因此可以用.then()处理其返回值。
Promise.resolve('success').then(function (value) {
    console.log(value);
});
-------output-------
Success
//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
});
  1. Promise.resolve()的另一个作用就是将thenable对象(即带有then方法的对象)转换为Promise对象。
var p1 = Promise.resolve({ 
    then: function (resolve, reject) { 
        resolve("this is an thenable object!");
    }
});
console.log(p1 instanceof Promise);     // => true

p1.then(function(value) {
    console.log(value);     // => this is an thenable object!
  }, function(e) {
    //not called
});
  1. 无论是在什么时候抛异常,只要promise状态变成resolved或rejected,状态不会再改变,这和新建promise是一样的。
//在回调函数前抛异常
var p1 = { 
    then: function(resolve) {
      throw new Error("error");
      resolve("Resolved");
    }
};

var p2 = Promise.resolve(p1);
p2.then(function(value) {
    //not called
}, function(error) {
    console.log(error);       // => Error: error
});
//在回调函数后抛异常
var p3 = { 
    then: function(resolve) {
        resolve("Resolved");
        throw new Error("error");
    }
};

var p4 = Promise.resolve(p3);
p4.then(function(value) {
    console.log(value);     // => Resolved
}, function(error) {
    //not called
});
6 .reject() - 立即执行Promise-reject

语法:Promise.reject(reason)

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

Promise.reject(new Error('error'));

/*******等同于*******/
new Promise(function (resolve, reject) {
    reject(new Error('error'));
});

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

* Promise 几个应用

Promise中的.then方法,可以接收构造函数中处理的状态的变化,并且分别对应执行。.then有两个函数参数,分别接收resolvedrejected的执行。
 简单来说, then就是定义resolvereject函数的,其resolve函数相当于:

function resolveFun(data) {
  // data 为 promise中resolve函数中所带的参数
}

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

1. 执行顺序

javascript代码

var promise = new Promise(function(resolve, reject) {
  console.log('before resolved');
  resolve();
  console.log('after resolved');
});

promise.then(function() {
  console.log('resolved');
});

console.log('outer');

-------output-------
// before resolved
// after resolved
// outer
// resolved
2.调用延时执行函数
function timeout (ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, 'done');   // 三个参数?往下看
  })
}

timeout(100).then((value) => {
  console.log(value)
})

// 输出 done

 该例写了一个延时调用函数,设置ms时间以后,才将Promise的状态修改为resolve,然后执行.then中的打印操作。
 这里埋了个点,比较有意思。当给setTimeout()传入大于两个参数时,从第三个开始代表的是传给延时执行函数的参数,想具体了解的可以戳这里

3. 异步加载图片
function loadImgAsync (url) {
  return new Promise(function (resolve, reject) {
    var img = new Image();
    
    image.onload = function () {
      resolve(image);
    };
    image.onerror = function() {
      reject(new Error('Could not load image at ' + url));
    }
    image.src = url;
  })
}

 上诉的例子中,先加载图片,如果图片加载成功,就调用resolve,否则调用reject

4. 用Promise实现一个Ajax操作
var url = 'https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-02-26/2017-06-10';

// 封装一个get请求的方法
function getJSON(url) {
    return new Promise(function(resolve, reject) {
        var XHR = new XMLHttpRequest();
        XHR.open('GET', url, true);
        XHR.send();

        XHR.onreadystatechange = function() {
            if (XHR.readyState == 4) {
                if (XHR.status == 200) {
                    try {
                        var response = JSON.parse(XHR.responseText);
                        resolve(response);
                    } catch (e) {
                        reject(e);
                    }
                } else {
                    reject(new Error(XHR.statusText));
                }
            }
        }
    })
}

getJSON(url).then(resp => console.log(resp));

 为了健壮性,处理了很多可能出现的异常,总之,成功了就resolve,失败了就reject。
 如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve函数的参数除了正常的值以外,还可能是另一个 Promise 实例

现在所有的库几乎都将ajax请求利用Promise进行了封装,因此我们在使用jQuery等库中的ajax请求时,都可以利用Promise来让我们的代码更加优雅和简单。这也是Promise最常用的一个场景

5. resolve参数为另一个Promise实例 - 难点
var p1 = new Promise(function(resolve, reject) {
  // ..some code
})
var p2 = new Promise(function(resolve, reject){
  // ..some code
  resolve(p1)
})

  上述中,p1和p2都是一个实例,定义了之后都立即执行,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作
注意:这时,p1的状态决定了p2的状态,如果p1的状态是pending,那么,p2的回调函数就会等待p1状态的改变,如果p1的状态已经是resolve或rejected,那么p2就会立即被执行。

很典型的一个例子

var p1 = new Promise(function(resolve, reject){
  setTimeout(() => reject(new Error('Fail')), 3000)
})
var p2 = new Promise(function(resolve, reject) {
  setTimeout(() => resolve(p1), 1000)
})

p2.then(result => conosle.log(result))
.catch(error => console.log(error)) 
//  建议大家看一下浏览器输出效果
// 输出 Error: fail

分析
 上述代码中,p1和p2被定义后都立即异步执行,p1执行3秒之后返回reject,p2执行1秒后返回resolve,但p2的返回结果是p1,所以,又要等待2秒p1返回结果,即p2的返回结果是“p1的返回结果”,这样大家就明白了,最终执行的其实是p1的结果reject,所以p2这里调用.catch而不是.then

注意:调用resolvereject并不会终结Promise的参数函数的执行。

new Promise(function(resolve, reject) {
  resolve(1);
  console.log(2);
}).then(val => {
  console.log(val)
}) 

//  2
//  1

 上面代码中,调用resolve(1)以后,后面的console.log(2)还是会执行,并且会首先打印出来。这是因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。虽然有这种策略,但我们一般习惯还是将其放在最后。

6. 聊天系统获取两个用户的信息 - Promise.all()
var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([p1, p2]).then(function (results) {
    console.log(results); // 获得一个Array: ['P1', 'P2']
});
7. 多个异步任务提高容错率 - Promise.race()
var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P1'
});

由于p1执行较快,Promise的then()将获得结果'P1'。p2仍在继续执行,但执行结果将被丢弃。

* Promise常见问题

到这里,相信你已学会使用Promise了,congratulation!

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了错误
/* 例4.1 */
function taskA() {
    console.log(x);
    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)    // 抛出错误,不继续Task A”
    .then(taskB)   // .then没有捕获A抛出的错,不打印 “Task B”
    .catch(onRejected)  // 捕获了A的错,打印错误信息
    .then(finalTask);   // 错误已经被捕获,执行resolve
    
-------output-------
Catch Error: A or B,ReferenceError: x is not defined
Final Task

来看一下该例的流程


很明显,A抛错时,会按照taskA → onRejected → finalTask这个流程来处理。A抛错后,若没有对它进行处理,状态就会维持rejected,taskB不会执行,直到catch了错误。

3. 每次调用then都会返回一个新创建的promise对象,而then内部只是返回的数据
//方法1:对同一个promise对象同时调用 then 方法
var p1 = new Promise(function (resolve) {
    resolve(100);
});
p1.then(function (value) {
    return value * 2;
});
p1.then(function (value) {
    return value * 2;
});
p1.then(function (value) {
    console.log("finally: " + value);
});
-------output-------
finally: 100

then的调用几乎是同时开始执行的,且传给每个then的value都是100,这种方法应当避免。正确的应该是采用链式调用。

//方法2:对 then 进行 promise chain 方式进行调用
var p2 = new Promise(function (resolve) {
    resolve(100);
});
p2.then(function (value) {
    return value * 2;
}).then(function (value) {
    return value * 2;
}).then(function (value) {
    console.log("finally: " + value);
});
-------output-------
finally: 400

或许上面这个案例你还没感觉,来看看这个:

function badAsyncCall(data) {
    var promise = Promise.resolve(data);
    promise.then(function(value) {
        //do something
        return value + 1;
    });
    return promise;
}
badAsyncCall(10).then(function(value) {
   console.log(value);          //想要得到11,实际输出10
});
-------output-------
10

正确的写法应该是:

function goodAsyncCall(data) {
    var promise = Promise.resolve(data);
    return promise.then(function(value) {
        //do something
        return value + 1;
    });
    return promise;
}
goodAsyncCall(10).then(function(value) {
   console.log(value);
});
-------output-------
11
4. 在异步回调中抛错,不会被catch到
// Errors thrown inside asynchronous functions will act like uncaught errors
var promise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    throw 'Uncaught Exception!';
  }, 1000);
});

promise.catch(function(e) {
  console.log(e);       //This is never called
});
5. promise状态变为resove或reject,就凝固了,不会再改变
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

花了两天工作之余的时间,总算写完了~~看了很多资源,借鉴了许多观点和例子,希望能帮到大家
参考资源:
阮一峰的ES6 教程
廖雪峰的官方网站
Promise迷你书
MDN Promise
廖雪峰的官网能直接在里面自定义代码尝试运行,挺有意思。

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

推荐阅读更多精彩内容

  • Promiese 简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,语法上说,Pr...
    雨飞飞雨阅读 3,329评论 0 19
  • 00、前言Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区...
    夜幕小草阅读 2,080评论 0 12
  • Promise的含义:   Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和...
    呼呼哥阅读 2,126评论 0 16
  • 本文适用的读者 本文写给有一定Promise使用经验的人,如果你还没有使用过Promise,这篇文章可能不适合你,...
    HZ充电大喵阅读 7,242评论 6 19
  • 海洋上浪花飞溅 鸥鸟飞翔 你从他方驶来 与我相遇 孤独的行程 都已走了很远 此刻的相遇 一扫了往日的寂寞 也许我们...
    昨日的酒阅读 235评论 0 4