JavaScript 初识Promise 对象

什么是Promise?

其实, Promise就是一个类,而且这个类已经成为ES6的标准,是 ECMAScript 6 规范的重要特性之一。这个类目前在chrome32、Opera19、Firefox29以上的版本都已经支持了,要想在所有浏览器上都用上的话就看看es6-promise吧。

ES6 的 Promises 是采用了 Promises/A+ 提案的一种实现。你现在能够找到的各种第三方实现,如果是完全兼容了 Promises/A+ 的,那么就和 ES6 的 Promises 是一致的(当然了,第三方实现可能会增加更多的 API,而 ES6 的实现只需要符合 Promises/A+ 提案的最基本要求即可)。目前, 当然,也有一些第三方内库实现了该功能,如: Q 、 when 、 WinJS 、 RSVP.js 等。

另外说句  Promise 在JQuery中 就是 $.Deferred 作为新特性首次出现在版本1.5中,

为什么要用 Promise?

1,Promise的第一个用途是能够很好地解决回调黑洞的问题

function fn1(){
console.info("this is 1") ;
}
function fn2(){
console.info("this is 2");
}
function fn3(){
console.info("this is 3") ;
}
//按照顺序调用
fn1() ;
fn2() ;
fn3() ;
//输出的结果:
//this is 1
//this is 2
//this is 3

  其实这样这做并不能保证fn1、fn2、fn3按照顺序的去执行的。大家都知道,javascritp是单线程执行的.假设

function fn1(){
setTimeout(function(){
console.info("this is 1") ; },5000);
}

 结果就不一定  按照顺序来了。按照异步的话,后面的 fn2 fn3的都得 写在f1的 setimeout 里面 。如果 复杂点 ,就会 不停地嵌套,陷入回调黑洞。如果解决

让输出的结果还是按照fn1->fn2->fn3的顺序去执行呢?

Promise 上场

function fn1(){

			    return   new Promise(function(resolve, reject){        //做一些异步操作
setTimeout(function(){
console.log('fn1 start。');
resolve('fn1');
}, 3000);
}); } function fn2(){ return new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log('fn2 start。');
resolve('fn2');
}, 1000);
}); } function fn3(){ return new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log('fn3 start。');
resolve('fn3');
}, 1000);
}); }

  

调用     fn1().then(fn2).then(fn3).then(function(data){ console.log(data);}); ;

//输出的结果是:

fn1 start。
fn2 start。
 fn3 start。
 fn3

 

var p1 = new Promise(function (resolve) {
setTimeout(function () {
resolve("Hello");
}, 3000);
}); var p2 = new Promise(function (resolve) {
setTimeout(function () {
resolve("World");
}, 1000);
}); Promise.all([p1, p2]).then(function (result) {
console.log(result); // ["Hello", "World"]
});
上面的例子模拟了传输两个数据需要不同的时长,虽然 p2 的速度比 p1 要快,但是 Promise.all 方法会按照数组里面的顺序将结果返回。

2,解决并发

假如我们要显示某一个页的10条记录,但是我们只有一个通过id获取记录的接口,这样我们就需要发送10个请求,并且所有请求都完成之后再将记录全部添加到页面之中,Promise在这个场景下使用是特别合适的

// ids要获取信息的所有记录id
// getRecordById获取记录的接口,返回promise
Promise.all(ids.map(getRecordById))
.then(showRecords)
.catch(function (e) {
console.log(e);
});

 或者 组件 下拉刷新,下拉刷新中,等到所有 ajax请求(别的请求)完毕以后,下拉刷新状态完成;才关闭刷新;

Promise.all([ajaxfn1, ajaxfn2, ajaxfn3]).then(function (results) {
console.log("success")
console.log(results);
}).catch(function(r){
console.log("err");
console.log(r);
});

  

Promise 如何工作?

JavaScript  初识Promise 对象

Promise 对象用来进行延迟( deferred )和异步( asynchronous )计算.。一个 Promise 处于以下四种状态之一:

pending: 还没有得到肯定或者失败结果,进行中
fulfilled: 成功的操作
rejected: 失败的操作
settled: 已被 fulfilled 或 rejected
Promise 对象有两个重要的方法,一个是 then ,另一个是 resolve :

then:将事务添加到事务队列中
resolve:开启流程,让整个操作从第一个事务开始执行

从上面图中可以看到 Promise/A+规范

  • Promise 对象有三种状态: Pending – Promise对象的初始状态,等到任务的完成或者被拒绝;Fulfilled – 任务执行完成并且成功的状态;Rejected – 任务执行完成并且失败的状态;这也是 Promise 「承诺」,表示其他手段无法改变。
  • Promise的状态只可能从“Pending”状态转到“Fulfilled”状态或者“Rejected”状态,而且不能逆向转换,同时“Fulfilled”状态和“Rejected”状态也不能相互转换;
  • Promise对象必须实现then方法,then是promise规范的核心,而且then方法也必须返回一个Promise对象,同一个Promise对象可以注册多个then方法,并且回调的执行顺序跟它们的注册顺序一致;
  • then方法接受两个回调函数,它们分别为:成功时的回调和失败时的回调;并且它们分别在:Promise由“Pending”状态转换到“Fulfilled”状态时被调用和在Promise由“Pending”状态转换到“Rejected”状态时被调用。
var promise = new Promise(function(resolve, reject) {
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
}); promise.then(function(value) {
// success
}, function(value) {
// failure
});

Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 方法和 reject 方法。

如果异步操作成功,则用 resolve 方法将 Promise 对象的状态,从「未完成」变为「成功」(即从 pending 变为 resolved);

如果异步操作失败,则用 reject 方法将 Promise 对象的状态,从「未完成」变为「失败」(即从 pending 变为 rejected)。

基本的 api

JavaScript  初识Promise 对象

  1. Promise.resolve() //成功
  2. Promise.reject()  //失败
  3. Promise.prototype.then()
  4. Promise.prototype.catch()
  5. Promise.all() // 所有的完成
  6. Promise.race() // 竞速,完成一个即可
 
JavaScript  初识Promise 对象

Promise 如何实现?

大体框架原理如下

var Promise = function() {
this.callbacks = [];
} Promise.prototype = {
constructor: Promise,
resolve: function(result) {
this.complete("resolve", result);
}, reject: function(result) {
this.complete("reject", result);
}, complete: function(type, result) {
while (this.callbacks[0]) {
this.callbacks.shift()[type](result);
}
}, then: function(successHandler, failedHandler) {
this.callbacks.push({
resolve: successHandler,
reject: failedHandler
}); return this;
}
}

测试代码

// test
var promise = new Promise(); var delay1 = function() {
setTimeout(function() {
promise.resolve('数据1');
}, 1000);
return promise;
}; var callback1 = function(re) { re = re + '数据2';
console.log(re);
promise.resolve(re);
}; var callback2 = function(re) { console.log(re + '数据3'); }; delay1().then(callback1).then(callback2);

上述的代码的 框架    实际中用于生产环境,这是不够的;

  • 能够返回值 无论返回值是fulfilled也好,还是被rejected也好,必须返回一个新的promise
  • 能够捕获抛出异常

Promise  用法之一 demo

JavaScript  初识Promise 对象

其他的参考

(function(window,undefined){

var PENDING = undefined, FULFILLED = 1, REJECTED = 2;

var isFunction = function(obj){
return 'function' === typeof obj;
}
var isArray = function(obj) {
return Object.prototype.toString.call(obj) === "[object Array]";
}
var isThenable = function(obj){
return obj && typeof obj['then'] == 'function';
} var transition = function(status,value){
var promise = this;
if(promise._status !== PENDING) return;
// 所以的执行都是异步调用,保证then是先执行的
setTimeout(function(){
promise._status = status;
publish.call(promise,value);
});
}
var publish = function(val){
var promise = this,
fn,
st = promise._status === FULFILLED,
queue = promise[st ? '_resolves' : '_rejects']; while(fn = queue.shift()) {
val = fn.call(promise, val) || val;
}
promise[st ? '_value' : '_reason'] = val;
promise['_resolves'] = promise['_rejects'] = undefined;
} var Promise = function(resolver){
if (!isFunction(resolver))
throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
if(!(this instanceof Promise)) return new Promise(resolver); var promise = this;
promise._value;
promise._reason;
promise._status = PENDING;
promise._resolves = [];
promise._rejects = []; var resolve = function(value){
transition.apply(promise,[FULFILLED].concat([value]));
}
var reject = function(reason){
transition.apply(promise,[REJECTED].concat([reason]));
} resolver(resolve,reject);
} Promise.prototype.then = function(onFulfilled,onRejected){
var promise = this;
// 每次返回一个promise,保证是可thenable的
return Promise(function(resolve,reject){
function callback(value){
var ret = isFunction(onFulfilled) && onFulfilled(value) || value;
if(isThenable(ret)){
ret.then(function(value){
resolve(value);
},function(reason){
reject(reason);
});
}else{
resolve(ret);
}
}
function errback(reason){
reason = isFunction(onRejected) && onRejected(reason) || reason;
reject(reason);
}
if(promise._status === PENDING){
promise._resolves.push(callback);
promise._rejects.push(errback);
}else if(promise._status === FULFILLED){ // 状态改变后的then操作,立刻执行
callback(promise._value);
}else if(promise._status === REJECTED){
errback(promise._reason);
}
});
} Promise.prototype.catch = function(onRejected){
return this.then(undefined, onRejected)
} Promise.prototype.delay = function(ms,val){
return this.then(function(ori){
return Promise.delay(ms,val || ori);
})
} Promise.delay = function(ms,val){
return Promise(function(resolve,reject){
setTimeout(function(){
resolve(val);
},ms);
})
} Promise.resolve = function(arg){
return Promise(function(resolve,reject){
resolve(arg)
})
} Promise.reject = function(arg){
return Promise(function(resolve,reject){
reject(arg)
})
} Promise.all = function(promises){
if (!isArray(promises)) {
throw new TypeError('You must pass an array to all.');
}
return Promise(function(resolve,reject){
var i = 0,
result = [],
len = promises.length,
count = len function resolver(index) {
return function(value) {
resolveAll(index, value);
};
} function rejecter(reason){
reject(reason);
} function resolveAll(index,value){
result[index] = value;
if( --count == 0){
resolve(result)
}
} for (; i < len; i++) {
promises[i].then(resolver(i),rejecter);
}
});
} Promise.race = function(promises){
if (!isArray(promises)) {
throw new TypeError('You must pass an array to race.');
}
return Promise(function(resolve,reject){
var i = 0,
len = promises.length; function resolver(value) {
resolve(value);
} function rejecter(reason){
reject(reason);
} for (; i < len; i++) {
promises[i].then(resolver,rejecter);
}
});
} window.Promise = Promise; })(window);
// see => http://promises-aplus.github.io/promises-spec/
// see => https://github.com/cujojs/when/blob/master/lib/makePromise.js ;(function(root, factory) {
if (typeof module !== 'undefined' && module.exports) {// CommonJS
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {// AMD / RequireJS
define(factory);
} else {
root.Promise = factory.call(root);
}
}(this, function() {
'use strict'; function Promise(resolver) {
if(!(this instanceof Promise)) return new Promise(resolver); this.status = 'pending';
this.value;
this.reason; // then may be called multiple times on the same promise
this._resolves = [];
this._rejects = []; if(isFn(resolver)) resolver(this.resolve.bind(this), this.reject.bind(this)); return this;
}; Promise.prototype.then = function(resolve, reject) {
var next = this._next || (this._next = Promise());
var status = this.status;
var x; if('pending' === status) {
isFn(resolve) && this._resolves.push(resolve);
isFn(reject) && this._rejects.push(reject);
return next;
} if('resolved' === status) {
if(!isFn(resolve)) {
next.resolve(resolve);
} else {
try {
x = resolve(this.value);
resolveX(next, x);
} catch(e) {
this.reject(e);
}
}
return next;
} if('rejected' === status) {
if(!isFn(reject)) {
next.reject(reject);
} else {
try {
x = reject(this.reason);
resolveX(next, x);
} catch(e) {
this.reject(e);
}
}
return next;
}
}; Promise.prototype.resolve = function(value) {
if('rejected' === this.status) throw Error('Illegal call.'); this.status = 'resolved';
this.value = value; this._resolves.length && fireQ(this); return this;
}; Promise.prototype.reject = function(reason) {
if('resolved' === this.status) throw Error('Illegal call.'); this.status = 'rejected';
this.reason = reason; this._rejects.length && fireQ(this); return this;
}; // shortcut of promise.then(undefined, reject)
Promise.prototype.catch = function(reject) {
return this.then(void 0, reject);
}; // return a promise with another promise passing in
Promise.cast = function(arg) {
var p = Promise(); if(arg instanceof Promise) return resolvePromise(p, arg);
else return Promise.resolve(arg);
}; // return a promise which resolved with arg
// the arg maybe a thanable object or thanable function or other
Promise.resolve = function(arg) {
var p = Promise(); if(isThenable(arg)) return resolveThen(p, arg);
else return p.resolve(arg);
}; Promise.all = function(promises) {
;
}; // return a promise which reject with reason
// reason must be an instance of Error object
Promise.reject = function(reason) {
if(!(reason instanceof Error)) throw Error('reason must be an instance of Error'); var p = Promise(); p.reject(reason); return p;
}; function resolveX(promise, x) {
if(x === promise) promise.reject(new Error('TypeError')); if(x instanceof Promise) return resolvePromise(promise, x);
else if(isThenable(x)) return resolveThen(promise, x);
else return promise.resolve(x);
}; function resolvePromise(promise, promise2) {
var status = promise2.status; if('pending' === status) {
promise2.then(promise.resolve.bind(promise), promise.reject.bind(promise));
}
if('resolved' === status) promise.resolve(promise2.value);
if('rejected' === status) promise.reject(promise2.reason); return promise;
}; function resolveThen(promise, thanable) {
var called;
var resolve = once(function(x) {
if(called) return;
resolveX(promise, x);
called = true;
});
var reject = once(function(r) {
if(called) return;
promise.reject(r);
called = true;
}); try {
thanable.then.call(thanable, resolve, reject);
} catch(e) {
if(!called) throw e;
else promise.reject(e);
} return promise;
}; function fireQ(promise) {
var status = promise.status;
var queue = promise['resolved' === status ? '_resolves' : '_rejects'];
var arg = promise['resolved' === status ? 'value' : 'reason'];
var fn;
var x; while(fn = queue.shift()) {
x = fn.call(promise, arg);
resolveX(promise._next, x);
} return promise;
}; function noop () {}; function isFn(fn) {
return 'function' === type(fn);
}; function isObj(o) {
return 'object' === type(o);
}; function type(obj) {
var o = {};
return o.toString.call(obj).replace(/\[object (\w+)\]/, '$1').toLowerCase();
}; function isThenable(obj) {
return obj && obj.then && isFn(obj.then);
}; function once(fn) {
var called; return function() {
if(called) return;
fn.apply(this, arguments);
called = true;
};
}; return Promise;
}));

http://www.dmyang.me/demos/promise/browser.html

https://github.com/yahoo/ypromise

/*!
Copyright 2013 Yahoo! Inc. All rights reserved.
Licensed under the BSD License.
http://yuilibrary.com/license/
*/ /*jslint expr: true */
/*global define */ (function (global, factory) {
var built = factory();
/* istanbul ignore else */
if (typeof module === 'object' && module) {
module.exports = built;
}
/* istanbul ignore next */
if (typeof define === 'function' && define.amd) {
define(factory);
}
global.PromisePolyfill = built;
global.Promise || (global.Promise = built);
}(typeof global !== 'undefined' ? global : /* istanbul ignore next */ this, function () { function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
} function assign(obj, props) {
for (var prop in props) {
/* istanbul ignore else */
if (props.hasOwnProperty(prop)) {
obj[prop] = props[prop];
}
}
} /**
A promise represents a value that may not yet be available. Promises allow
you to chain asynchronous operations, write synchronous looking code and
handle errors throughout the process.
This constructor takes a function as a parameter where you can insert the logic
that fulfills or rejects this promise. The fulfillment value and the rejection
reason can be any JavaScript value. It's encouraged that rejection reasons be
error objects
<pre><code>
var fulfilled = new Promise(function (resolve) {
resolve('I am a fulfilled promise');
});
var rejected = new Promise(function (resolve, reject) {
reject(new Error('I am a rejected promise'));
});
</code></pre>
@class Promise
@constructor
@param {Function} fn A function where to insert the logic that resolves this
promise. Receives `resolve` and `reject` functions as parameters.
This function is called synchronously.
**/
function Promise(fn) {
if (!(this instanceof Promise)) {
throw new TypeError(this + 'is not a promise');
}
if (typeof fn !== 'function') {
throw new TypeError('Promise resolver ' + fn + ' is not a function');
} var resolver = new Resolver(); /**
A reference to the resolver object that handles this promise
@property _resolver
@type Object
@private
*/
this._resolver = resolver; try {
fn(function (value) {
resolver.resolve(value);
}, function (reason) {
resolver.reject(reason);
});
} catch (e) {
resolver.reject(e);
}
} assign(Promise.prototype, {
/**
Schedule execution of a callback to either or both of "fulfill" and
"reject" resolutions for this promise. The callbacks are wrapped in a new
promise and that promise is returned. This allows operation chaining ala
`functionA().then(functionB).then(functionC)` where `functionA` returns
a promise, and `functionB` and `functionC` _may_ return promises.
Asynchronicity of the callbacks is guaranteed.
@method then
@param {Function} [callback] function to execute if the promise
resolves successfully
@param {Function} [errback] function to execute if the promise
resolves unsuccessfully
@return {Promise} A promise wrapping the resolution of either "resolve" or
"reject" callback
**/
then: function (callback, errback) {
// using this.constructor allows for customized promises to be
// returned instead of plain ones
var resolve, reject,
promise = new this.constructor(function (res, rej) {
resolve = res;
reject = rej;
}); this._resolver._addCallbacks(
typeof callback === 'function' ?
Promise._makeCallback(promise, resolve, reject, callback) : resolve,
typeof errback === 'function' ?
Promise._makeCallback(promise, resolve, reject, errback) : reject
); return promise;
}, /*
A shorthand for `promise.then(undefined, callback)`.
Returns a new promise and the error callback gets the same treatment as in
`then`: errors get caught and turned into rejections, and the return value
of the callback becomes the fulfilled value of the returned promise.
@method catch
@param [Function] errback Callback to be called in case this promise is
rejected
@return {Promise} A new promise modified by the behavior of the error
callback
*/
'catch': function (errback) {
return this.then(undefined, errback);
}
}); /**
Wraps the callback in another function to catch exceptions and turn them
into rejections.
@method _makeCallback
@param {Promise} promise Promise that will be affected by this callback
@param {Function} fn Callback to wrap
@return {Function}
@static
@private
**/
Promise._makeCallback = function (promise, resolve, reject, fn) {
// callbacks and errbacks only get one argument
return function (valueOrReason) {
var result; // Promises model exception handling through callbacks
// making both synchronous and asynchronous errors behave
// the same way
try {
// Use the argument coming in to the callback/errback from the
// resolution of the parent promise.
// The function must be called as a normal function, with no
// special value for |this|, as per Promises A+
result = fn(valueOrReason);
} catch (e) {
// calling return only to stop here
reject(e);
return;
} if (result === promise) {
reject(new TypeError('Cannot resolve a promise with itself'));
return;
} resolve(result);
};
}; /*
Ensures that a certain value is a promise. If it is not a promise, it wraps it
in one.
This method can be copied or inherited in subclasses. In that case it will
check that the value passed to it is an instance of the correct class.
This means that `PromiseSubclass.resolve()` will always return instances of
`PromiseSubclass`.
@method resolve
@param {Any} Any object that may or may not be a promise
@return {Promise}
@static
*/
Promise.resolve = function (value) {
if (value && value.constructor === this) {
return value;
}
/*jshint newcap: false */
return new this(function (resolve) {
/*jshint newcap: true */
resolve(value);
});
}; /*
A shorthand for creating a rejected promise.
@method reject
@param {Any} reason Reason for the rejection of this promise. Usually an Error
Object
@return {Promise} A rejected promise
@static
*/
Promise.reject = function (reason) {
/*jshint newcap: false */
var promise = new this(function () {});
/*jshint newcap: true */ // Do not go through resolver.reject() because an immediately rejected promise
// always has no callbacks which would trigger an unnecessary warning
promise._resolver._result = reason;
promise._resolver._status = 'rejected'; return promise;
}; /*
Returns a promise that is resolved or rejected when all values are resolved or
any is rejected. This is useful for waiting for the resolution of multiple
promises, such as reading multiple files in Node.js or making multiple XHR
requests in the browser.
@method all
@param {Any[]} values An array of any kind of values, promises or not. If a value is not
@return [Promise] A promise for an array of all the fulfillment values
@static
*/
Promise.all = function (values) {
var Promise = this;
return new Promise(function (resolve, reject) {
if (!isArray(values)) {
reject(new TypeError('Promise.all expects an array of values or promises'));
return;
} var remaining = values.length,
i = 0,
length = values.length,
results = []; function oneDone(index) {
return function (value) {
results[index] = value; remaining--; if (!remaining) {
resolve(results);
}
};
} if (length < 1) {
return resolve(results);
} for (; i < length; i++) {
Promise.resolve(values[i]).then(oneDone(i), reject);
}
});
}; /*
Returns a promise that is resolved or rejected when any of values is either
resolved or rejected. Can be used for providing early feedback in the UI
while other operations are still pending.
@method race
@param {Any[]} values An array of values or promises
@return {Promise}
@static
*/
Promise.race = function (values) {
var Promise = this;
return new Promise(function (resolve, reject) {
if (!isArray(values)) {
reject(new TypeError('Promise.race expects an array of values or promises'));
return;
} // just go through the list and resolve and reject at the first change
// This abuses the fact that calling resolve/reject multiple times
// doesn't change the state of the returned promise
for (var i = 0, count = values.length; i < count; i++) {
Promise.resolve(values[i]).then(resolve, reject);
}
});
}; /**
Forces a function to be run asynchronously, but as fast as possible. In Node.js
this is achieved using `setImmediate` or `process.nextTick`. In YUI this is
replaced with `Y.soon`.
@method async
@param {Function} callback The function to call asynchronously
@static
**/
/* istanbul ignore next */
Promise.async = typeof setImmediate !== 'undefined' ?
function (fn) {setImmediate(fn);} :
typeof process !== 'undefined' && process.nextTick ?
process.nextTick :
function (fn) {setTimeout(fn, 0);}; /**
Represents an asynchronous operation. Provides a
standard API for subscribing to the moment that the operation completes either
successfully (`fulfill()`) or unsuccessfully (`reject()`).
@class Promise.Resolver
@constructor
**/
function Resolver() {
/**
List of success callbacks
@property _callbacks
@type Array
@private
**/
this._callbacks = []; /**
List of failure callbacks
@property _errbacks
@type Array
@private
**/
this._errbacks = []; /**
The status of the operation. This property may take only one of the following
values: 'pending', 'fulfilled' or 'rejected'.
@property _status
@type String
@default 'pending'
@private
**/
this._status = 'pending'; /**
This value that this promise represents.
@property _result
@type Any
@private
**/
this._result = null;
} assign(Resolver.prototype, {
/**
Resolves the promise, signaling successful completion of the
represented operation. All "onFulfilled" subscriptions are executed and passed
the value provided to this method. After calling `fulfill()`, `reject()` and
`notify()` are disabled.
@method fulfill
@param {Any} value Value to pass along to the "onFulfilled" subscribers
**/
fulfill: function (value) {
var status = this._status; if (status === 'pending' || status === 'accepted') {
this._result = value;
this._status = 'fulfilled';
} if (this._status === 'fulfilled') {
this._notify(this._callbacks, this._result); // Reset the callback list so that future calls to fulfill()
// won't call the same callbacks again. Promises keep a list
// of callbacks, they're not the same as events. In practice,
// calls to fulfill() after the first one should not be made by
// the user but by then()
this._callbacks = []; // Once a promise gets fulfilled it can't be rejected, so
// there is no point in keeping the list. Remove it to help
// garbage collection
this._errbacks = null;
}
}, /**
Resolves the promise, signaling *un*successful completion of the
represented operation. All "onRejected" subscriptions are executed with
the value provided to this method. After calling `reject()`, `resolve()`
and `notify()` are disabled.
@method reject
@param {Any} reason Value to pass along to the "reject" subscribers
**/
reject: function (reason) {
var status = this._status; if (status === 'pending' || status === 'accepted') {
this._result = reason;
this._status = 'rejected';
} if (this._status === 'rejected') {
this._notify(this._errbacks, this._result); // See fulfill()
this._callbacks = null;
this._errbacks = [];
}
}, /*
Given a certain value A passed as a parameter, this method resolves the
promise to the value A.
If A is a promise, `resolve` will cause the resolver to adopt the state of A
and once A is resolved, it will resolve the resolver's promise as well.
This behavior "flattens" A by calling `then` recursively and essentially
disallows promises-for-promises.
This is the default algorithm used when using the function passed as the
first argument to the promise initialization function. This means that
the following code returns a promise for the value 'hello world':
var promise1 = new Promise(function (resolve) {
resolve('hello world');
});
var promise2 = new Promise(function (resolve) {
resolve(promise1);
});
promise2.then(function (value) {
assert(value === 'hello world'); // true
});
@method resolve
@param [Any] value A regular JS value or a promise
*/
resolve: function (value) {
if (this._status === 'pending') {
this._status = 'accepted';
this._value = value; if ((this._callbacks && this._callbacks.length) ||
(this._errbacks && this._errbacks.length)) {
this._unwrap(this._value);
}
}
}, /**
If `value` is a promise or a thenable, it will be unwrapped by
recursively calling its `then` method. If not, the resolver will be
fulfilled with `value`.
This method is called when the promise's `then` method is called and
not in `resolve` to allow for lazy promises to be accepted and not
resolved immediately.
@method _unwrap
@param {Any} value A promise, thenable or regular value
@private
**/
_unwrap: function (value) {
var self = this, unwrapped = false, then; if (!value || (typeof value !== 'object' &&
typeof value !== 'function')) {
self.fulfill(value);
return;
} try {
then = value.then; if (typeof then === 'function') {
then.call(value, function (value) {
if (!unwrapped) {
unwrapped = true;
self._unwrap(value);
}
}, function (reason) {
if (!unwrapped) {
unwrapped = true;
self.reject(reason);
}
});
} else {
self.fulfill(value);
}
} catch (e) {
if (!unwrapped) {
self.reject(e);
}
}
}, /**
Schedule execution of a callback to either or both of "resolve" and
"reject" resolutions of this resolver. If the resolver is not pending,
the correct callback gets called automatically.
@method _addCallbacks
@param {Function} [callback] function to execute if the Resolver
resolves successfully
@param {Function} [errback] function to execute if the Resolver
resolves unsuccessfully
**/
_addCallbacks: function (callback, errback) {
var callbackList = this._callbacks,
errbackList = this._errbacks; // Because the callback and errback are represented by a Resolver, it
// must be fulfilled or rejected to propagate through the then() chain.
// The same logic applies to resolve() and reject() for fulfillment.
if (callbackList) {
callbackList.push(callback);
}
if (errbackList) {
errbackList.push(errback);
} switch (this._status) {
case 'accepted':
this._unwrap(this._value);
break;
case 'fulfilled':
this.fulfill(this._result);
break;
case 'rejected':
this.reject(this._result);
break;
}
}, /**
Executes an array of callbacks from a specified context, passing a set of
arguments.
@method _notify
@param {Function[]} subs The array of subscriber callbacks
@param {Any} result Value to pass the callbacks
@protected
**/
_notify: function (subs, result) {
// Since callback lists are reset synchronously, the subs list never
// changes after _notify() receives it. Avoid calling Y.soon() for
// an empty list
if (subs.length) {
// Calling all callbacks after Promise.async to guarantee
// asynchronicity. Because setTimeout can cause unnecessary
// delays that *can* become noticeable in some situations
// (especially in Node.js)
Promise.async(function () {
var i, len; for (i = 0, len = subs.length; i < len; ++i) {
subs[i](result);
}
});
}
} }); Promise.Resolver = Resolver; return Promise; }));

  

https://github.com/taylorhakes/promise-polyfill

finally

Promise.prototype.finally = function (callback) {
var P = this.constructor;
return this.then(function (value) {
return P.resolve(callback()).then(function () {
return value;
});
}, function (reason) {
return P.resolve(callback()).then(function () {
throw reason;
});
});
};
const promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(() => {
console.log(3)
})
console.log(4)
运行结果: 1
2
4
3
解释:Promise 构造函数是同步执行的,promise.then 中的函数是异步执行的。
const promise = new Promise((resolve, reject) => {
resolve('success1')
reject('error')
resolve('success2')
}) promise
.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})
运行结果: 1
then: success1
解释:构造函数中的 resolve 或 reject 只有第一次执行有效,多次调用没有任何作用,呼应代码二结论:promise 状态一旦改变则不能再变。
process.nextTick(() => {
console.log('nextTick')
})
Promise.resolve()
.then(() => {
console.log('then')
})
setImmediate(() => {
console.log('setImmediate')
})
console.log('end')
运行结果: end
nextTick
then
setImmediate
解释:process.nextTick 和 promise.then 都属于 microtask,而 setImmediate 属于 macrotask,在事件循环的 check 阶段执行。事件循环的每个阶段(macrotask)之间都会执行 microtask,事件循环的开始会先执行一次 microtask。

JavaScript  初识Promise 对象

JavaScript  初识Promise 对象

JavaScript  初识Promise 对象

参考文档;

https://www.html5rocks.com/en/tutorials/es6/promises/

http://www.infoq.com/cn/articles/promise-a-misunderstanding-and-practical
https://github.com/linkFly6/Promise/blob/master/src/Promise.js
https://github.com/hanan198501/promise/blob/master/src/promise.js

如何实现Promise http://liuwanlin.info/shi-xian-promise/
Javascript 中的神器——Promise http://www.jianshu.com/p/063f7e490e9a
推荐的 Primise 插件 https://github.com/stefanpenner/es6-promise
推荐这篇文章 https://www.promisejs.org/implementing/
美团的剖析 Promise 之基础篇 http://tech.meituan.com/promise-insight.html
Promises/A+ https://promisesaplus.com/
MDN-Promise https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

http://liubin.org/promises-book/#Promise.all

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>promise </title>
<meta http-equiv="X-UA-Compatible" content="edge,chrome=1" />
<meta http-equiv="Cache-Control" content="no-siteapp" /> <!-- 避免转码 -->
<meta name="keywords" content=""/>
<meta name="description" content=""/>
<meta name="viewport" content="initial-scale=1.0,user-scalable=no,maximum-scale=1,width=device-width" />
<meta name="renderer" content="webkit|ie-comp|ie-stand">
<meta name="apple-itunes-app" content="app-id=932758491"> </head>
<body> <style>
*{ margin: 0 ;padding: 0;}
.one{ margin: 10px; padding: 10px; border: 1px solid red; margin-top: 40px;} </style>
promise 测试
<script> ;(function(root, factory) {
if (typeof module !== 'undefined' && module.exports) {// CommonJS
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {// AMD / RequireJS
define(factory);
} else {
root.Promise = factory.call(root);
}
}(this, function() {
'use strict'; function noop() {} // Polyfill for Function.prototype.bind
function bind(fn, thisArg) {
return function() {
fn.apply(thisArg, arguments);
};
} function Promise(fn) {
if(!(this instanceof Promise)) return new Promise(fn);
if (typeof fn !== 'function') throw new TypeError('not a function'); this._state = 0;
this._handled = false;
this._value = undefined;
this._deferreds = []; doResolve(fn, this);
} function handle(self, deferred) {
while (self._state === 3) {
self = self._value;
}
if (self._state === 0) {
self._deferreds.push(deferred);
return;
}
self._handled = true;
Promise._immediateFn(function() {
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
if (cb === null) {
(self._state === 1 ? resolve : reject)(deferred.promise, self._value);
return;
}
var ret;
try {
ret = cb(self._value);
} catch (e) {
reject(deferred.promise, e);
return;
}
resolve(deferred.promise, ret);
});
} function resolve(self, newValue) {
try {
// Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
if (newValue === self)
throw new TypeError('A promise cannot be resolved with itself.');
if (
newValue &&
(typeof newValue === 'object' || typeof newValue === 'function')
) {
var then = newValue.then;
if (newValue instanceof Promise) {
self._state = 3;
self._value = newValue;
finale(self);
return;
} else if (typeof then === 'function') {
doResolve(bind(then, newValue), self);
return;
}
}
self._state = 1;
self._value = newValue;
finale(self);
} catch (e) {
reject(self, e);
}
} function reject(self, newValue) {
self._state = 2;
self._value = newValue;
finale(self);
} function finale(self) {
if (self._state === 2 && self._deferreds.length === 0) {
Promise._immediateFn(function() {
if (!self._handled) {
Promise._unhandledRejectionFn(self._value);
}
});
} for (var i = 0, len = self._deferreds.length; i < len; i++) {
handle(self, self._deferreds[i]);
}
self._deferreds = null;
} function Handler(onFulfilled, onRejected, promise) {
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
this.onRejected = typeof onRejected === 'function' ? onRejected : null;
this.promise = promise;
} /**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*/
function doResolve(fn, self) {
var done = false;
try {
fn(
function(value) {
if (done) return;
done = true;
resolve(self, value);
},
function(reason) {
if (done) return;
done = true;
reject(self, reason);
}
);
} catch (ex) {
if (done) return;
done = true;
reject(self, ex);
}
} Promise.prototype['catch'] = function(onRejected) {
return this.then(undefined, onRejected);
}; Promise.prototype.then = function(onFulfilled, onRejected) {
var prom = new this.constructor(noop); handle(this, new Handler(onFulfilled, onRejected, prom));
return prom;
}; Promise.all = function(arr) {
return new Promise(function(resolve, reject) {
if (!arr || typeof arr.length === 'undefined') throw new TypeError('Promise.all accepts an array');
var args = Array.prototype.slice.call(arr);
if (args.length === 0) return resolve([]);
var remaining = args.length; function res(i, val) {
try {
if (val && (typeof val === 'object' || typeof val === 'function')) {
var then = val.then;
if (typeof then === 'function') {
then.call(
val,
function(val) {
res(i, val);
},
reject
);
return;
}
}
args[i] = val;
if (--remaining === 0) {
resolve(args);
}
} catch (ex) {
reject(ex);
}
} for (var i = 0; i < args.length; i++) {
res(i, args[i]);
}
});
}; Promise.resolve = function(value) {
if (value && typeof value === 'object' && value.constructor === Promise) {
return value;
} return new Promise(function(resolve) {
resolve(value);
});
}; Promise.reject = function(value) {
return new Promise(function(resolve, reject) {
reject(value);
});
}; Promise.race = function(values) {
if (!values || typeof values.length === 'undefined') throw new TypeError('Promise.race accepts an array'); return new Promise(function(resolve, reject) {
for (var i = 0, len = values.length; i < len; i++) {
Promise.resolve(values[i]).then(resolve, reject);
}
});
}; Promise.prototype.finally = function _finally(callback) {
var promise = this;
var constructor = promise.constructor; return promise.then(function (value) {
return constructor.resolve(callback()).then(function () {
return value;
});
}, function (reason) {
return constructor.resolve(callback()).then(function () {
throw reason;
});
});
}; // Use polyfill for setImmediate for performance gains
Promise._immediateFn = typeof setImmediate !== 'undefined' ?
function (fn) {setImmediate(fn);} :
typeof process !== 'undefined' && process.nextTick ?
process.nextTick :
function (fn) {setTimeout(fn, 0);}; Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
if (typeof console !== 'undefined' && console) {
console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
}
}; return Promise; })); </script> <div id="j_promise_dom" class="one">promise domreday</div>
<script> 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 节点加载完毕');
}); console.dir(Promise);
</script> <div id="j_promise_queue" class="one">promise 队列</div> <script> function fn1(){ return new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log('fn1 start。');
resolve('fn1');
}, 3000);
}); } function fn2(){ return new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log('fn2 start。');
resolve('fn2');
}, 1000);
}); } function fn3(){ return new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log('fn3 start。');
resolve('fn3');
}, 1000);
}); } //做饭
function cook(){
console.log('开始做饭。');
var p = new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log('做饭完毕!');
resolve('鸡蛋炒饭');
}, 1000);
});
return p;
} //吃饭
function eat(data){
console.log('开始吃饭:' + data);
var p = new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log('吃饭完毕!');
resolve('一块碗和一双筷子');
}, 2000);
});
return p;
} function wash(data){
console.log('开始洗碗:' + data);
var p = new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log('洗碗完毕!');
resolve('最后 干净的碗筷');
}, 2000);
});
return p;
} document.getElementById("j_promise_queue").addEventListener("click",function(){ // cook().then(function(data){
// return eat(data);
// }).then(function(data){
// return wash(data);
// }).then(function(data){
// console.log(data);
// }); 
// });
//
cook().then(eat).then(wash).then(function(data){ console.log(data);}); 
fn1().then(fn2).then(fn3).then(function(data){ console.log(data);}); ; });
</script> <div id="j_promise_all" class="one">promise all</div>
<script> var p1 = new Promise(function (resolve) {
setTimeout(function () {
resolve("Hello");
}, 2000);
}); var p2 = new Promise(function (resolve) {
setTimeout(function () {
resolve("World");
}, 1000);
}); function pms_all1(str) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log(str+'执行任务1');
resolve('执行任务1成功');
}, 2000);
});
} function pms_all2() {
return Promise(function(resolve, reject) {
setTimeout(function() {
console.log('执行任务2');
resolve('执行任务2成功');
}, 1000);
});
} function pms_all3() {
return new Promise(function(resolve, reject) {
// setTimeout(function() {
console.log('执行任务3');
resolve('执行任务3成功');
// }, 2000);
});
} document.getElementById("j_promise_all").addEventListener("click",function(){ Promise.all([pms_all1("我是参数1啊"), pms_all2(), pms_all3()]).then(function(data) {
console.log(data+' 所有数据加载完毕');
//console.log();
}).finally(function(){
console.log("all finally");
});; Promise.all([p1, p2]).then(function (result) {
console.log(result); // ["Hello", "World"]
}); Promise.all([
Promise.resolve(11),
Promise.resolve(22),
Promise.resolve(33)
]).then(function(e){
console.log(e); //[11,22,33]
}); //该方法接一个迭代器(如数组等), 返回一个新的Promise对象. 如果迭代器中所有的Promise对象都被实现, 那么, 返回的Promise对象状态为”fulfilled”, 反之则为”rejected”. 概念上类似Array.prototype.every.
Promise.all([
Promise.resolve(111),
Promise.resolve(222),
Promise.reject(333)
]).catch(function(e){
console.log(e); //333
}); //then(null, function() {}) 就等同于catch(function() {}) Promise.all([
Promise.resolve(1111),
Promise.resolve(2222),
Promise.reject(3333) // reject('该prormise已被拒绝'); 用于捕获并处理异常. 无论是程序抛出的异常, 还是主动reject掉Promise自身, 都会被catch捕获到.
]).then(null,function(e){
console.log(e); //333
}); }); </script> <div id="j_promise_rice" class="one">promise rice</div> <script> var pms_race1 = new Promise(function(resolve, reject) {
setTimeout(function() { resolve('First!'); }, 5000);
}); var pms_race2 = new Promise(function(resolve, reject) {
setTimeout(function() { resolve('Second!'); }, 1000);
}); document.getElementById("j_promise_rice").addEventListener("click",function(){ Promise.race([pms_race1, pms_race2]).then(function(result) {
console.log('谁先 race Then: ', result);
//console.log(somedata); //此处的somedata未定义 也就是说进到catch方法里面去了,而且把错误原因传到了reason参数中。即便是有错误的代码也不会报错了,这与我们的try/catch语句有相同的功能。
}).catch(function(result) { console.log('race Catch: ', result);
}).finally(function(){
console.log("rece finally");
}); Promise.race([1, Promise.reject(2)]).then(function(res){
console.log('11promise fulfilled:', res);
}).catch(function(reason){
console.log('11promise reject:', reason);
});
// promise fulfilled: 1
//如果调换以上参数的顺序, 结果将输出 “promise reject: 2”. 可见对于状态稳定的Promise(fulfilled 或 rejected状态), 哪个排第一, 将返回哪个.
//Promise.race适用于多者中取其一的场景, 比如同时发送多个请求, 只要有一个请求成功, 那么就以该Promise的状态作为最终的状态, 该Promise的值作为最终的值, 包装成一个新的Promise对象予以返回.
Promise.race([Promise.reject(2),1]).then(function(res){
console.log('22promise fulfilled:', res);
}).catch(function(reason){
console.log('22promise reject:', reason);
});
//promise reject: 2 }) // Promise._immediateFn=function(){
// alert(22);
// }
// alert(11); </script> </script>
</body>
</html>

 

 function fn(num,str) {
return new Promise(function(resolve, reject) {
// setTimeout(function(){
if (typeof num == 'number') {
resolve([num,str]);
} else {
reject(num);
}
// }, 1000);
}).then(function(res2) {
console.log('成功 resolve 参数 '+res2[0]+' 是一个number值,另一个参数 '+res2[1]);
}, function(res2) {
console.log('失败 reject 参数 '+res2+' 不是number值');
})
} document.getElementById("sa3").addEventListener("click",function(){ fn('hahha');
fn(1234,'参2数'); })

  

Promise.resolve(42);

//等价于

new Promise(function(resolve){

resolve(42);

});

Promise.reject(new Error("出错了"));

//等价于

new Promise(function(resolve,reject){

reject(new Error("出错了"));

});

理解 JavaScript 的 async/await

async function testAsync() {
return "hello async";
} const result = testAsync();
console.log(result);
function getSomething() {
return "something";
} async function testAsync() {
return Promise.resolve("hello async");
} async function test() {
const v1 = await getSomething();
const v2 = await testAsync();
console.log(v1, v2);
} test();

现在举例,用 setTimeout 模拟耗时的异步操作,先来看看不用 async/await 会怎么写看个实例

function takeLongTime() {
return new Promise(resolve => {
setTimeout(() => resolve("long_time_value"), 1000);
});
} takeLongTime().then(v => {
console.log("got", v);
});

如果改用 async/await 呢,会是这样

function takeLongTime() {
return new Promise(resolve => {
setTimeout(() => resolve("long_time_value"), 1000);
});
} async function test() {
const v = await takeLongTime();
console.log(v);
} test();

 

眼尖的同学已经发现 takeLongTime() 没有申明为 async。实际上,takeLongTime() 本身就是返回的 Promise 对象,加不加 async 结果都一样,如果没明白,请回过头再去看看上面的“async 起什么作用”。

又一个疑问产生了,这两段代码,两种方式对异步调用的处理(实际就是对 Promise 对象的处理)差别并不明显,甚至使用 async/await 还需要多写一些代码,那它的优势到底在哪?

async/await 的优势在于处理 then 链

单一的 Promise 链并不能发现 async/await 的优势,但是,如果需要处理由多个 Promise 组成的 then 链的时候,优势就能体现出来了(很有意思,Promise 通过 then 链来解决多层回调的问题,现在又用 async/await 来进一步优化它)。

假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。我们仍然用 setTimeout 来模拟异步操作:

/**
* 传入参数 n,表示这个函数执行的时间(毫秒)
* 执行的结果是 n + 200,这个值将用于下一步骤
*/
function takeLongTime(n) {
return new Promise(resolve => {
setTimeout(() => resolve(n + 200), n);
});
} function step1(n) {
console.log(`step1 with ${n}`);
return takeLongTime(n);
} function step2(n) {
console.log(`step2 with ${n}`);
return takeLongTime(n);
} function step3(n) {
console.log(`step3 with ${n}`);
return takeLongTime(n);
}

现在用 Promise 方式来实现这三个步骤的处理

function doIt() {
console.time("doIt");
const time1 = 300;
step1(time1)
.then(time2 => step2(time2))
.then(time3 => step3(time3))
.then(result => {
console.log(`result is ${result}`);
console.timeEnd("doIt");
});
} doIt(); // c:\var\test>node --harmony_async_await .
// step1 with 300
// step2 with 500
// step3 with 700
// result is 900
// doIt: 1507.251ms

  

输出结果 result 是 step3() 的参数 700 + 200 = 900doIt() 顺序执行了三个步骤,一共用了 300 + 500 + 700 = 1500 毫秒,和 console.time()/console.timeEnd() 计算的结果一致。

如果用 async/await 来实现呢,会是这样

async function doIt() {
console.time("doIt");
const time1 = 300;
const time2 = await step1(time1);
const time3 = await step2(time2);
const result = await step3(time3);
console.log(`result is ${result}`);
console.timeEnd("doIt");
} doIt();

 

现在把业务要求改一下,仍然是三个步骤,但每一个步骤都需要之前每个步骤的结果。

function step1(n) {
console.log(`step1 with ${n}`);
return takeLongTime(n);
} function step2(m, n) {
console.log(`step2 with ${m} and ${n}`);
return takeLongTime(m + n);
} function step3(k, m, n) {
console.log(`step3 with ${k}, ${m} and ${n}`);
return takeLongTime(k + m + n);
}

  

这回先用 async/await 来写:

async function doIt() {
console.time("doIt");
const time1 = 300;
const time2 = await step1(time1);
const time3 = await step2(time1, time2);
const result = await step3(time1, time2, time3);
console.log(`result is ${result}`);
console.timeEnd("doIt");
} doIt(); // c:\var\test>node --harmony_async_await .
// step1 with 300
// step2 with 800 = 300 + 500
// step3 with 1800 = 300 + 500 + 1000
// result is 2000
// doIt: 2907.387ms

  

别的用法

async function executeAsyncTask () {
const valueA = await functionA()
const valueB = await functionB(valueA)
return function3(valueA, valueB)
}

  

另外一个常见的例子,loading 确实是等待请求都结束完才清除的。那么,正常的处理是怎样的呢?

async function correctDemo() {
let p1 = sleep(1000);
let p2 = sleep(1000);
let p3 = sleep(1000);
await Promise.all([p1, p2, p3]);
console.log('clear the loading~');
}
correctDemo();// clear the loading~

容错处理 try catch

async function asyncTask () {
try {
const valueA = await functionA()
const valueB = await functionB(valueA)
const valueC = await functionC(valueB)
return await functionD(valueC)
} catch (err) {
logger.error(err)
}
}

  

常用的解决

使用Promise是这样的:

const makeRequest = () =>
getJSON()
.then(data => {
console.log(data)
return "done"
}) makeRequest()

使用Async/Await是这样的:

const makeRequest = async () => {
console.log(await getJSON())
return "done"
} makeRequest()

  

Promise是根据函数式编程的范式,对异步过程进行了一层封装。
async/await是基于协程的机制,是真正的“保存上下文,控制权切换 ... ... 控制权恢复,取回上下文”这种机制,是对异步过程更精确的一种描述。

上一篇:python全栈开发-Day5 集合


下一篇:Android学习之Http使用Post方式进行数据提交(普通数据和Json数据)