一、概述

ES2015 Promise函数是对PromiseA+标准的实现,并且严格遵守该标准。

二、APIs

2.1 创建Promise对象

Promise构造函数的参数是个包含两个参数的函数,并且该函数的参数分别对应resolve, reject操作(resolve,reject也是函数)。并且只能通过这两个参数改变Promise的状态。

function asynOperation(){
    var promise = new Promise(function(resolve, reject){
        setTimeout(function(){
            reject(1,2,3); // 调用reject操作,并传递reason参数。虽然传递多个参数,但只把第一个作为reson值,符合Promise A+标准
        }, 3000)
    });
    return promise;
    
}
asynOperation().then(function(value){
    console.log('ES2015 Promise: resolved and value is ' + value);
}, function(reason){
    console.log('ES2015 Promise: rejected and reason is ' + reason);
})

上面的代码用zpotojs实现:

/*
    ZeptoJS Deferred
*/
function AsynOperation1(){
    var deferred = $.Deferred();
    setTimeout(function(){
        deferred.reject(1, 2, 3);
    }, 3000);
    return deferred.promise();
}
AsynOperation1().then(function(val){
    console.log('Fullfilled: ' + val);
}, function(reason){
    console.log('Rejected: ' + reason);
})

2.2 Promise.prototype.catch

catch方法用来添加Rejected状态回调,是then方法的一种子集。

asynOperation().catch(function(reason){
})
// 等价于
asynOperation().then(undefined, function(reason){
})

2.3 Promise.reject(reason)

Promise构造函数的静态方法,创建个Promise对象并以reason值调用reject句柄函数。换句话说该静态函数返回个处于“Rejected”状态的Promise对象。

var p = Promise.reject(1);
// 等价于
var p = new Promise(function(resolve, reject){
  reject(1);
})

2.4 Promise.resolve(value)

Promise构造函数的静态函数,创建一个Promise对象并以value值作为参数调用resolve句柄函数。换句话说该静态函数返回个处于“fullfilled"状态的Promise对象。

var p = Promise.resolve(1);
// 等价于
var = new Promise(function(resolve){
  resolve(1);
})

2.5 Promise.all(iterable)

2.5.1 介绍

Promise构造函数的静态函数,参数是Promise对象组成的可迭代数据集合。创建个Promise对象,并且参数指定的所以Promise都解决后,该Promise对象该被解决,反之如果其中存在rejected的Promise,则该Promise对象被rejected。
定义个异步操作:

function asynOperation(value, reason){
    var promise = new Promise(function(resolve, reject){
        setTimeout(function(){
            value === undefined ? reject(reason) : resolve(value);
        }, 3000)
    });
    return promise;
    
}

var p1 = asynOperation(1),
    p2 = asynOperation(2),
    p3 = asynOperation(3);
Promise.all([p1, p2, p3]).then(function(value){
    console.log('all resolved: value is ' + value); // value是[1, 2, 3] 
})

如果参数元素中发生rejected操作,则立马reject返回的Promise:

var p1 = asynOperation(1),
    p2 = asynOperation(undefined, 2), // reject操作
    p3 = asynOperation(undefined, 3); // reject操作
p2.catch(function(reson){
    console.log('p2')
})
p3.catch(function(reson){
    console.log('p3')
})
Promise.all([p1, p2, p3]).then(function(value){
    console.log('all resolved: value is ' + value)
}, function(reason){
    console.log('one rejected: reason is ' + reason); // reson值等于发生reject操作的promise对象的reason,即p2
})

显示结果:

clipboard.png

2.5.2 对比$.when

Promise.all和$.when很类似。主题功能差不多,参数传递方式不一样:ES2015中把所有的Promise对象的value构建个数组,而$.when是分别传递的。

function AsynOperation1(value, reason){
    var deferred = $.Deferred();
    setTimeout(function(){
        value === undefined ? deferred.reject(reason) : deferred.resolve(value);
    }, 3000);
    return deferred.promise();
}
var p1 = AsynOperation1(1),
    p2 = AsynOperation1(2),
    p3 = AsynOperation1(3);
$.when(p1, p2, p3).then(function(value){
    console.log('resolved ' + value); // 留意Value的值
}, function(reason){
    console.log('rejected ' + reason);
})

2.6 Promise.race(iterable)

Promise构造函数的静态函数,参数是Promise对象构成的可迭代对象,创建个Promise对象。当参数任意Promise对象fullfilled(rejected)时,则立马fullfill(reject)该Promise对象。

var p1 = asynOperation(1),
    p2 = asynOperation(undefined, 2),
    p3 = asynOperation(undefined, 3);
Promise.race([p1, p2, p3]).then(function(value){
    console.log('one resolved: value is ' + value); // Value=1
}, function(reason){
    console.log('one rejected: reason is ' + reason)
})

三、micro-task

Promise的回调通过micor-task实现的。

console.log(1)
setTimeout(function(){ console.log(2)}, 0)
Promise.resolve().then(function(){console.log(3)})
console.log(4)
// 输出应该是:1 4 3 2

四、对比Promise A+ 标准

大部分Promise A+标准的实现对"Promise解决过程"标准的实现略有不同,比如zeptoJs Deferred就没考虑thenable情况。但是ES2015 Promise函数完全严格遵守Promise A+标准,包含对“Promise 解决过程的实现。下面举例说明实现Promise解决过程(The Promise Resolution Procedure):[[Resolve]](promise, x)

3.3.1 如果promise对象和x相等,则用TypeError对象作为reason 拒绝promise对象

var p1 = asynOperation(1);
var p2 = p1.then(function(value){
        console.log('resolved: ' + value);
        return p2; // 返回p2对象,
    },function(reason){
        console.log('rejected: ' + reason);
        }};
    });
p2.then(function(value){
    console.log('resolved: ' + value);
},function(reason){
    console.log('rejected: ' + reason);

clipboard.png

var p1 = asynOperation(1);
var p2 = p1.then(function(value){
        console.log('resolved: ' + value);
         return asynOperation(2); // 返回个Promise对象,以该Prmoise对象状态决定p2的状态
    },function(reason){
        console.log('rejected: ' + reason);
    });
p2.then(function(value){
    console.log('resolved: ' + value);
},function(reason){
    console.log('rejected: ' + reason);
})

clipboard.png

3.3.3 如果x是thenable,promise状态的改变取决于x.then方法的执行中resolve,reject句柄的调用情况。并不受x.then返回值的影响。

  • A:then方法没有调用resolve或者reject,并且没有返回值,则不会改变promose的状态
var p1 = asynOperation(1);
var p2 = p1.then(function(value){
        console.log('resolved: ' + value);
        return {name: 'john', then: function(resolve, reject){ // 返回值是个thenable对象,但是then方法没有调用resolve或者reject,并且没有返回值
            
        }};
    },function(reason){
        console.log('rejected: ' + reason);
        
    });
p2.then(function(value){
    console.log('resolved: ' + value);
},function(reason){
    console.log('rejected: ' + reason);
})

clipboard.png

  • B:then方法中调用resolve句柄,用新value y递归调用Promise解决过程:[[Resolve]](promise, y)
var p1 = asynOperation(1);
var p2 = p1.then(function(value){
        console.log('resolved: ' + value);
        return {name: 'john', then: function(resolve, reject){
            resolve(2); // then方法中调用resolve句柄,用新value递归调用Promise解决过程
        }};
    },function(reason){
        console.log('rejected: ' + reason);
    });
p2.then(function(value){
    console.log('resolved: ' + value);
},function(reason){
    console.log('rejected: ' + reason);
})
  • C:then方法中调用reject句柄,则用相同的reason拒绝promise.
var p1 = asynOperation(1);
var p2 = p1.then(function(value){
        console.log('resolved: ' + value);
        return {name: 'john', then: function(resolve, reject){
            reject(2); // 调用reject句柄
        }};
    },function(reason){
        console.log('rejected: ' + reason);
    });
p2.then(function(value){
    console.log('resolved: ' + value);
},function(reason){
    console.log('rejected: ' + reason);
})
  • D:then方法中多次调用resolve, reject句柄,只有第一个调用有效,其他的忽略
  • E:then方法中抛异常,如果抛异常之前已经调用resolve或者reject句柄,则忽略该异常,反之则以该异常为reason拒绝promise

2.3.4 其他情况,则用x作为value完成promise。

var p1 = asynOperation(undefined, 1); // 拒绝的promise
var p2 = p1.then(function(value){
        console.log('resolved: ' + value);
        return {name: 'john'};
    },function(reason){
        console.log('rejected: ' + reason);
        return 2;
    });
p2.then(function(value){
    console.log('resolved: ' + value);
},function(reason){
    console.log('rejected: ' + reason);
})

还没结束

一道关于Promise应用的面试题

参考

  1. 是时候使用promise了
  2. avaScript Promise迷你书(中文版)
  3. MDN
  4. Javascript Promise All Example | Promise.All() In JS

普拉斯强
2.7k 声望53 粉丝

Coder