Promise小书(长文)

前言

正文主要参照了JavaScript Promise迷你开,链接以文末与任何参考一起列出。

promise基础

Promise是异步编程的平种缓解方案。ES6
Promise的正式来Promises/A+社区,它发为数不少版的落实。

Promise比传统的缓解方案(回调函数和波)更客观和更强有力,可以避免回调地狱。使用Promise来归并处理异步操作,更有着语义化、易于掌握、有利保障。

Promise接口的核心思维是吃异步操作返回一个Promise对象,我们可以对这目标进行局部操作。

其三种状态及少栽转移路径

Promise对象仅出三栽状态。

  • 异步操作“未就”,promise对象刚吃创造后的初始化状态(unresolved,Promises/A+中称pending)
  • 异步操作“已到位”(resolved,Promises/A+中称fulfilled)
  • 异步操作“失败”(rejected)

立马三种植的状态的成形路径只有发些许栽。

  • 异步操作由“未就”到“已到位”
  • 异步操作由“未到位”到“失败”。

这种转变只能发出同样坏,一旦当前状态成为“已到位”或“失败”,就表示非会见再度闹新的状态变化了。因此,Promise对象的最后结果就出一定量种植。

异步操作成,Promise对象传回一个值,状态变为resolved。

异步操作失败,Promise对象抛来一个破绽百出,状态变为rejected。

api简介

眼下根本发生三种植档次

  1. 构造函数(Constructor)

创造一个promise实例:

var promise = new Promise(function (resolve, reject) {
    // 异步处理
    // 处理结束后、调用resolve 或 reject
})
  1. 实例方法(Instance Method)

    promise.then(onFulfilled, onRejected)

    promise.catch(onRejected)

  2. 静态方法(Static Method)

Promise.all()、 Promise.race()、Promise.resolve()、Promise.reject()

创建promise对象

吃Promise构造函数传递一个函数fn作为参数实例化即可。这个函数fn有点儿只参数(resolve同reject),在fn中指定异步等处理:

  • 处理结果正常的讲话,调用resolve(处理结果值)
  • 处理结果错误的语句,调用reject(Error对象)。

 // 创建promise对象基本形式
    var promise = new Promise(function (resolve, reject) {
        // ... some code

        if (/* 异步操作成功 */) {
            resolve(value)
        } else {
            reject(error)
        }
    })

    // 将图片加载转为promise形式
    var preloadImage = function (path) {
        return new Promise(function (resolve, reject) {
            var image = new Image()
            image.onload  = resolve
            image.onerror = reject
            image.src = path
        })
    }

    // 创建XHR的promise对象
    function getURL (URL) {
        return new Promise(function (resolve, reject) {
            var req = new XMLHttpRequest()
            req.open('GET', URL, true)
            req.onload = function () {
                if (req.status === 200) {
                    resolve(req.responseText)
                } else {
                    reject(new Error(req.statusText))
                }
            }
            req.onerror = function () {
                reject(new Error(req.statusText))
            }
            req.send()
        })
    }

    // 运行示例
    var URL = 'http://httpbin.org/get'
    getURL(URL)
        .then(function onFulfilled (value){
            console.log(value)
        })
        .catch(function onRejected (error){
            console.error(error)
        })

getURL只来于经XHR取得结果状态吧200常常才见面调用resolve。也就算是独发生多少获得成功时,而别情况(取得失败)时虽然会调用reject方法。

resolve(req.responseText)在response的内容遭加入了参数。resolve方法的参数并无专门的条条框框,基本上把要招给回调函数参数放进去就可了。(then方法可收起及者参数值)

啊promise对象上加拍卖方式

为promise对象上加拍卖办法主要发生以下简单种:

  • promise对象吃resolve时之处理(onResolved)
  • promise对象被reject时之处理(onRejected)

深受resolve后的处理,可以以.then方法吃传播想如果调用的函数:

var URL = 'http://httpbin.org/get'
getURL(URL).then(function onResolved(value){ 
    console.log(value)
})

为reject后的拍卖,可以以.then的老二个参数或者是在.catch方法中安装想要调用的函数。

var URL = 'http://httpbin.org/status/500'
getURL(URL)
    .then(function onResolved(value){
        console.log(value)
    })
    .catch(function onRejected(error){ 
        console.error(error)
    })

.catch只是promise.then(undefined,
onRejected)的别名而已,如下代码也可以好同样的法力。

getURL(URL).then(onResolved, onRejected)

Promise.resolve

1)new Promise的快捷方式

静态方法Promise.resolve(value)可以看是new
Promise()方法的快捷方式。Promise.resolve(value)返回一个态由为定value决定的Promise对象。如果该值是一个Promise对象,则直回到该目标;如果该值是thenable对象(见下有2),返回的Promise对象的最终状态由then方法执行决定;否则的口舌(该value为空,基本项目或者无带then方法的靶子),返回的Promise对象状态为resolved,并且以欠value传递给相应之then方法。

为此和new
Promise()方法并无完全一致。Promise.resolve接收一个promise对象会一直回到这个目标。而new
Promise()总是新生成一个promise对象。

var p1 = Promise.resolve(1)

var p2 = Promise.resolve(p1)

var p3 = new Promise(function (resolve, reject) {
    resolve(p1)
})

console.log(p1 === p2) // true
console.log(p1 === p3) // false

常用Promise.resolve()快速初始化一个promise对象。

Promise.resolve(42).then(function (value) {
    console.log(value)
})

2)Promise.resolve方法外一个企图就是是以thenable对象转换为promise对象。

嘿是thenable对象?Thenable对象好当是看似Promise对象,拥有名为.then方法的对象。和类数组的定义一般。

产生安thenable对象?主要是ES6之前有成百上千仓房实现了Promise,其中有多和ES6
Promise规范并无雷同,我们遂这些同ES6吃的promise对象类似而同时生异样的目标也thenable对象。如jQuery中的ajax()方法返回的对象。

// 将thenable对象转换promise对象
var promise = Promise.resolve($.ajax('/json/comment.json')) // => promise对象
promise.then(function (value) {
   console.log(value)
})

Promise.reject()

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

比如Promise.reject(new Error(‘出错了’))就是下代码的语法糖形式:

new Promise(function (resolve, reject) {
    reject(new Error('出错了'))
})

Promise.all

Promise.all方法用于将大半独Promise实例,包装成一个初的Promise实例。

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

地方代码中,Promise.all方法接受一个数组作为参数,p1、p2、p3都是Promise实例,如果不是,就见面优先调用Promise.resolve方法,将参数转为Promise实例,再进一步处理。

p的状态由p1、p2、p3决定,分成两种状态。

(1)只生p1、p2、p3的状态且成resolved,p的状态才会化为resolved,此时p1、p2、p3的回值组成一个勤组,传递给p的回调函数。

(2)只要p1、p2、p3之受到发出一个被rejected,p的状态就改为rejected,此时先是单叫reject的实例的回值,会传送给p的回调函数。

传送给Promise.all的promise并无是一个个底依次执行的,而是同时开始、并行执行的。

// `delay`毫秒后执行resolve
function timerPromisefy (delay) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(delay)
        }, delay)
    })
}

var startDate = Date.now()

// 所有promise变为resolve后程序退出
Promise.all([
    timerPromisefy(1),
    timerPromisefy(32),
    timerPromisefy(64),
    timerPromisefy(128)
]).then(function (values) {
    console.log(Date.now() - startDate + 'ms')
    // 约128ms
    console.log(values)   // [1, 32, 64, 128]
})

由上述结果好看看,传递让Promise.all的promise并无是一个个之一一执行的,而是以开始、并行执行的。

如若这些promise全部串行处理的话,那么用等1ms → 等待32ms → 等待64ms →
等待128ms ,全部履完毕要大约225ms的时。

Promise.race

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

以及Promise.all类似,但是如果p1、p2、p3之受到来一个实例率先改变状态,p的状态就跟着变动。那个率先改变之Promise实例的回值,就传递给p的回调函数。

// `delay`毫秒后执行resolve
function timerPromisefy(delay) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(delay)
        }, delay)
    })
}

// 任何一个promise变为resolve或reject的话程序就停止运行
Promise.race([
    timerPromisefy(1),
    timerPromisefy(32),
    timerPromisefy(64),
    timerPromisefy(128)
]).then(function (value) {
    console.log(value) // => 1
})

下我们重新来看望在第一单promise对象变成确定(resolved)状态后,它之后的promise对象是不是还当此起彼伏运行:

var winnerPromise = new Promise(function (resolve) {
    setTimeout(function () {
        console.log('this is winner')
        resolve('this is winner')
    }, 4)
})

var loserPromise = new Promise(function (resolve) {
    setTimeout(function () {
        console.log('this is loser')
        resolve('this is loser')
    }, 1000)
})

// 第一个promise变为resolve后程序停止
Promise.race([winnerPromise, loserPromise]).then(function (value) {
    console.log(value) // => 'this is winner'
})

履行方代码的话,我们会看winnter和loser
promise对象的setTimeout方法都见面实施了,console.log也会见分别出口它们的信。

也就是说,Promise.race在首先独promise对象变为Fulfilled之后,并无见面撤销其他promise对象的推行。

每当ES6
Promises规范中,也尚无收回(中断)promise对象实施的定义,我们亟须使力保promise最终进resolve
or
reject状态有。也就是说Promise并无适用于状态恐怕会见稳定不更换的拍卖。也时有发生部分类库提供了对promise进行取消的操作。

Promise的兑现类库(Library)

是因为不少浏览器不支持ES6
Promises,我们得有些叔正在实现的和Promise兼容的类库。

慎选Promise类库首先要考虑的是否有Promises/A+兼容性。

Promises/A+是ES6 Promises的前身,Promise的then也是由于社区的正式而来。

这些类库主要出个别种植:Polyfill和扩大类库

1)Polyfill

  • jakearchibald/es6-promise:应用最广大的一个仓房,推荐用是库房。
  • yahoo/ypromise:这是一个单独版本的
    YUI 的 Promise Polyfill。
  • getify/native-promise-only:严格遵循ES6
    Promises的正规化设计,没有上加在专业着莫概念之效果。

2)Promise扩展类库

  • kriskowal/q:
    Q.promise,这个大家应还比熟悉了。Angularjs中之$q也是叫之启发。
  • petkaantonov/bluebird:这个类库除了兼容Promise规范以外,还扩大了注销promise对象的周转,取得promise的运转速度,以及错误处理的壮大检测等非常丰富的效能,此外其当落实上还当性质问题下了老充分之素养。

Q等文档里详细介绍了Q的Deferred和jQuery里之Deferred有怎么样异同,以及若怎么进行搬迁等都进展了详尽的求证。

点滴独有效之附加措施

1)done()

Promise对象的回调链,不管坐then方法还是catch方法结尾,要是最后一个措施抛来左,都来或无法捕捉到(因为Promise内部的失实不会见打肿脸充胖子泡到全局)。因此,我们可提供一个done方法,总是处在回调链的尾端,保证抛出任何可能出现的荒谬。

'use strict'
if (typeof Promise.prototype.done === 'undefined') {
    Promise.prototype.done = function (onFulfilled, onRejected) {
        this.then(onFulfilled, onRejected)
            .catch(function (error) {
                setTimeout(function () {
                    throw error
                }, 0)
            })
    }
}

// 调用
asyncFunc()
    .then(f1)
    .catch(r1)
    .then(f2)
    .done()

打上面代码可以看看done有以下简单只性状。

  • done中出现的错误会为当作十分抛出
  • 终结Promise chain

那么她是哪些拿死抛到Promise的外场的为?其实这里我们采用的凡于setTimeout中以throw方法,直接拿特别抛给了表。

// setTimeout的回调函数中抛出异常
try {
    setTimeout(function callback () {
        throw new Error('error')
    }, 0)
} catch (error) {
    console.error(error)
}

坐异步的callback中丢掉来底百般不会见给抓获,上面例子中之差不会见受抓走。

ES6
Promises和Promises/A+等当筹划上并无对Promise.prototype.done做出其他规定,但是为什么许多类库都提供了拖欠方法的贯彻啊?

第一是预防编码时忘记行使catch方法处理好导致错误排查大窘迫的题目。由于Promise的try-catch机制,异常或者会见受间消化掉。这种错误被中间消化的题目呢叫名unhandled
rejection,从字面上看就是当Rejected时无找到相应处理的意思。

function JSONPromise (value) {
    return new Promise(function (resolve) {
        resolve(JSON.parse(value))
    })
}

// 运行示例
var string = '{}'
JSONPromise(string).then(function (object) {
    conosle.log(object)
})

在这个例子里,我们错把console拼成了conosle,因此会出如下错误:

ReferenceError: conosle is not defined

而是在chrome中实测查找这种不当就相当精准了。所以先之所以jQuery的时刻用过done,后来在其实项目被并不曾下了done方法。

2)finally()

finally方法用于指定不管Promise对象最后状态如何,都见面实行的操作。它与done方法的无比酷分别,它接受一个平常的回调函数作为参数,该函数不随便什么都必执行。

Promise.prototype.finally = function (callback) {
  let P = this.constructor
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  )
}

其一还是蛮有因此之,我们常常于ajax无论成功还是失败后还设关loading。我一般以是库房promise.prototype.finally。

Promise只能进行异步操作?

var promise = new Promise(function (resolve) {
    console.log(1) // 1
    resolve(3)
})

promise.then(function(value){
    console.log(value) // 3
})

console.log(2) // 2

推行方的代码,会挨个输出1,2,3。首先new
Promise中之函数会马上执行,然后是外界的console.log(2),最后是then回调中之函数。

由promise.then执行之上promise对象都是规定状态,从程序及说对回调函数进行共同调用也是实用的。

但是即使在调用promise.then注册回调函数的时光promise对象已经是规定的状态,Promise也会见盖异步的办法调用该回调函数,这是在Promise设计上之确定方针。为什么而如此吧?

眼看干到同调用和异步调用同时有导致的繁杂。

function onReady (fn) {
    var readyState = document.readyState
    if (readyState === 'interactive' || readyState === 'complete') {
        fn()
    } else {
        window.addEventListener('DOMContentLoaded', fn)
    }
}

onReady(function () {
    console.log('DOM fully loaded and parsed')
})

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

点的代码如果在调用onReady之前DOM已经载入的口舌:对回调函数进行共同调用。

设若当调用onReady之前DOM还尚无载入的说话:通过挂号DOMContentLoaded事件监听器来针对回调函数进行异步调用。

据此,如果及时段代码在自文件中出现的职务不同,在控制台上打印的log消息顺序吧会不同。

为化解这题目,我们得选取统一运用异步调用的法:

function onReady (fn) {
    var readyState = document.readyState
    if (readyState === 'interactive' || readyState === 'complete') {
        setTimeout(fn, 0)
    } else {
        window.addEventListener('DOMContentLoaded', fn)
    }
}

onReady(function () {
    console.log('DOM fully loaded and parsed')
})

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

关于这个题材,在Effective
JavaScript的第67项不要对异步回调函数进行共同调用中吗来详尽介绍:

  • 绝不可知针对异步回调函数(即使以数额已经就绪)进行共同调用。
  • 一经对异步回调函数进行联合调用的话,处理顺序可能会见与预期不符,可能带来意料之外的究竟。
  • 对异步回调函数进行联合调用,还可能造成栈溢出要大处理错乱等题材。
  • 假如想以明天有时刻调用异步回调函数的口舌,可以采用setTimeout等异步API。

以避免上述被同时使用并、异步调用可能滋生的混乱问题,Promise在规范上规定Promise只能以异步调用方式。

由于Promise保证了每次调用都是为异步方式开展的,所以我们于实际上编码中未欲调用setTimeout来协调实现异步调用:

function onReadyPromise () {
    return new Promise(function (resolve, reject) {
        var readyState = document.readyState
        if (readyState === 'interactive' || readyState === 'complete') {
            resolve()
        } else {
            window.addEventListener('DOMContentLoaded', resolve)
        }
    })
}

onReadyPromise().then(function () {
    console.log('DOM fully loaded and parsed')
})

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

异步操作顺序问题

前面Promise.resolve()章节的老三独promise,我们省那行各个是如何的?

var p1 = Promise.resolve(1)

var p2 = Promise.resolve(p1)

var p3 = new Promise(function (resolve, reject) {
    resolve(p1)
})

var p4 = new Promise(function (resolve, reject) {
    reject(p1)
})

p3.then(function (value) {
    console.log('p3 : ' + value)
})

p2.then(function (value) {
    console.log('p2 : ' + value)
})

p4.then(function (value) {
    console.log('p4-1 : ' + value)
}, function (value) {
    console.log('p4-1 : ' + value)
})

p4.then(function (value) {
    console.log('p4-2 : ' + value)
}).catch(function (value) {
    console.log('p4-2 : ' + value)
})

p1.then(function (value) {
    console.log('p1 : ' + value)
})

咱们以可比新的浏览器控制高输出会发现各个吗2,4-1,1,4-2,3(测试发现chrome55、56中则是第一打印出3)。这个不清楚怎么解释了,为什么p3会最后执行?暂时尚未找到什么保险的资料,有大神知道的话,请评论指出。

Promise chain(Promise方法链)

Promise chain流程

function taskA () {
    console.log('Task A')
}

function taskB () {
    console.log('Task B')
}

function onRejected (error) {
    console.log('Catch Error: A or B', error)
}

function finalTask () {
    console.log('Final Task')
}

var promise = Promise.resolve()
promise
    .then(taskA)
    .then(taskB)
    .catch(onRejected)
    .then(finalTask)

于上述代码中,我们没啊then方法指定第二独参数(onRejected),可以像下这样来掌握:

then:注册onResolved时的回调函数

catch:注册onRejected时之回调函数

1)taskA、taskB都尚未生大,会随taskA → taskB →
finalTask这个流程来进行处理

2)taskA没有来很,taskB发生很,会遵循taskA → taskB → onRejected →
finalTask这个流程来拓展处理

3)taskA发生特别,会以taskA → onRejected →
finalTask这个流程来开展拍卖,TaskB是不会见于调用的

function taskA () {
    console.log('Task A')
    throw new Error('throw Error @ Task A')
}

function taskB () {
    console.log('Task B') // 不会被调用
}

function onRejected (error) {
    console.log(error) // => 'throw Error @ Task A'
}

function finalTask () {
    console.log('Final Task')
}

var promise = Promise.resolve()
promise
    .then(taskA)
    .then(taskB)
    .catch(onRejected)
    .then(finalTask)

在本例中我们在taskA中动用了throw方法故意制造了一个要命。但以其实被怀念积极进行onRejected调用的时段,应该回到一个Rejected状态的promise对象。

promise chain中争传递参数?

倘Task A想被Task
B传递一个参数该怎么处置吧?其实非常简单,只要以taskA中return一个值,这个值会作为参数传递给taskB。

function doubleUp (value) {
    return value * 2
}

function increment (value) {
    return value + 1
}

function output (value) {
    console.log(value) // => (1 + 1) * 2
}

var promise = Promise.resolve(1)
promise
    .then(increment)
    .then(doubleUp)
    .then(output)
    .catch(function (error) {
        // promise chain中出现异常的时候会被调用
        console.error(error)
    })

每个方法中return的价不仅只是局限为字符串或者数值类,也可以是目标或promise对象等繁杂类型。

return的值会由Promise.resolve(return的返回值)进行相应的卷入处理,因此无论是回调函数中见面回来一个争的价值,最终then的结果还是回一个新创造的promise对象。

也就是说,Promise的then方法不仅是注册一个回调函数那么简单,它还见面将回调函数的回来值进行更换,创建并回到一个promise对象。

安已promise chain

当以Promise处理部分复杂逻辑的历程被,我们有时会惦记要当发生某种错误后哪怕终止实施Promise链后面有的代码。

而是Promise本身并没供这么的法力,一个操作,要么成功,要么失败,要么跳反至then里,要么跳反到catch里。

切切实实怎么开,请查看这篇稿子自哪已少 Promise
链说由。

每次调用then都见面回到一个初创办的promise对象

自打代码上新一收押,aPromise.then(…).catch(…)像是对初的aPromise对象开展了洋洋洒洒的计链调用。

可事实上无论是then还是catch方法调用,都回了一个初的promise对象。

var aPromise = new Promise(function (resolve) {
    resolve(100)
})

var thenPromise = aPromise.then(function (value) {
    console.log(value)
})

var catchPromise = thenPromise.catch(function (error) {
    console.error(error)
})

console.log(aPromise !== thenPromise) // => true
console.log(thenPromise !== catchPromise) // => true

实施方代码,证明了then和catch都归了同调用者不同之promise对象。知道了马上点,我们虽挺轻掌握下面两栽调用方法的分别:

// 1: 对同一个promise对象同时调用 `then` 方法
var aPromise = new Promise(function (resolve) {
    resolve(100)
})

aPromise.then(function (value) {
    return value * 2
})

aPromise.then(function (value) {
    return value * 2
})

aPromise.then(function (value) {
    console.log('1: ' + value) // => 100
})

// vs

// 2: 对 `then` 进行 promise chain 方式进行调用
var bPromise = new Promise(function (resolve) {
    resolve(100)
})

bPromise.then(function (value) {
    return value * 2
}).then(function (value) {
    return value * 2
}).then(function (value) {
    console.log('2: '' + value) // => 100 * 2 * 2
})

下面是一个出于方1遭遇之then用法导致的可比便于出现的可怜有代表性的反倒模式之例证:

// then的错误使用方法
function badAsyncCall() {
    var promise = Promise.resolve()
    promise.then(function() {
        // 任意处理
        return newVar
    })
    return promise
}

这种写法有那么些题目,首先以promise.then中发出的老不见面让表面捕获,此外,也未克博得then的回来值,即使其发出归值。

不仅仅then和catch都回来了和调用者不同的promise对象,Promise.all和Promise.race,他们还见面收下一组promise对象也参数,并返回一个暨收参数不同之、新的promise对象。

动用then的老二个参数还是catch处理好?

事先我们说了 .catch也堪知道吧promise.then(undefined,
onRejected)。那么下这有限栽方法进行错误处理有什么区别也?

function throwError (value) {
    // 抛出异常
    throw new Error(value)
}

// <1> onRejected不会被调用
function badMain (onRejected) {
    return Promise.resolve(42).then(throwError, onRejected)
}

// <2> 有异常发生时onRejected会被调用
function goodMain (onRejected) {
    return Promise.resolve(42).then(throwError).catch(onRejected)
}

// 运行示例
badMain(function () {
    console.log("BAD")
})

goodMain(function () {
    console.log("GOOD")
})

于方的代码中,badMain是一个不顶好之兑现方式(但为无是说其来差不多老),goodMain则是一个会好好之展开错误处理的本子。

为什么说badMain不好吗?,因为尽管咱于.then的次只参数中指定了用于错误处理的函数,但实际它却未克捕获第一单参数onResolved指定的函数(本例为
throwError)里面出现的谬误。

也就是说,这时候就throwError抛来了挺,onRejected指定的函数也无见面让调用(即未会见输出”BAD”字样)。

跟之相对的凡,goodMain的代码则以了throwError →
onRejected的调用流程。这时候throwError中冒出特别的言语,在会让方法链中的下一个道,即.catch所擒获,进行相应的错误处理。

.then方法中之onRejected参数所指定的回调函数,实际上对的是那个promise对象或之前的promise对象,而无是针对.then方法中指定的率先单参数,即onResolved所指向的靶子,这吗是then和catch表现各异之案由。

1)使用promise.then(onResolved, onRejected)的话

当onResolved中发出甚的言语,在onRejected中凡捕获不顶之非常的。

2)在promise.then(onResolved).catch(onRejected)的动静下

then中来的怪能当.catch中捕获

3).then和.catch在本质上是没区分之

用分场合以。

俺们得注意要代码类似badMain那样的话,就可能出现程序不会见随预想运行的景,从而不可知科学的拓错误处理。

IE8及IE8以下catch兼容问题

IE8及IE8以下就就引入了Promise的polyfill,使用catch方法仍然会并发identifier
not found的语法错误。

就是怎么回事呢?实际上就与catch是ECMAScript的保留字(Reserved Word)有关。

以ECMAScript
3面临保留字是休可知同日而语靶子的属性名使用的。而IE8及以下版本都是基于ECMAScript
3兑现之,因此不可知拿catch作为性能来采取,也不怕未能够修类似promise.catch()的代码,因此即使涌出了identifier
not found这种语法错误了。

苟当代浏览器都支持ECMAScript 5,而在ECMAScript
5挨保留字都属IdentifierName,也得看成属性名使用了。

点标记法(dot notation)要求对象的性质必须是行之标识符(在ECMAScript
3遇虽免可知以保留字)。

但下中括号标记法(bracket
notation)的话,则足以用非官标识符作为对象的属于性名使用。

var promise = Promise.reject(new Error('message'))
promise['catch'](function (error) {
    console.error(error)
})

由于catch标识符可能会见招致问题应运而生,因此有些类库(Library)也祭了caught作为函数号称,而函数要就的干活是一模一样的。

与此同时许多压缩工具自带了拿promise.catch转换为promise[‘catch’]的效益,所以可能无检点之间也能协助咱缓解者题材。

使用reject而不是throw

var promise = new Promise(function (resolve, reject) {
    throw new Error("message")
})

promise.catch(function (error) {
    console.error(error) // => "message"
})

地方代码其实并没有啊问题,但是有零星单糟糕的地方:

第一是盖咱们很难区分throw是咱们积极弃出来的,还是因实在的外异常导致的。

副本来就是暨调节没有涉嫌的地方,throw时虽会硌调试器的break行为,会惊动浏览器的调试器中break的效应的健康下。

故而采用reject会比使用throw安全。

再议Promise.resolve和Thenable

前我们曾摆了Promise.resolve能以thenable对象转化为promise对象。接下来我们再度看看用thenable对象转换为promise对象是意义都能实际做来什么业务。

盖Web Notification为例,普通应用回调函数方式如下:

function notifyMessage (message, options, callback) {
    if (Notification && Notification.permission === 'granted') {
        var notification = new Notification(message, options)
        callback(null, notification)
    } else if (Notification.requestPermission) {
        Notification.requestPermission(function (status) {
            if (Notification.permission !== status) {
                Notification.permission = status
            }
            if (status === 'granted') {
                var notification = new Notification(message, options)
                callback(null, notification)
            } else {
                callback(new Error('user denied'))
            }
        })
    } else {
        callback(new Error('doesn\'t support Notification API'))
    }
}

// 运行实例
// 第二个参数是传给 `Notification` 的option对象
notifyMessage('Hi!', {}, function (error, notification) {
    if (error) {
        return console.error(error)
    }
    console.log(notification) // 通知对象
})

下Promise改写回调:

function notifyMessage (message, options, callback) {
    if (Notification && Notification.permission === 'granted') {
        var notification = new Notification(message, options)
        callback(null, notification)
    } else if (Notification.requestPermission) {
        Notification.requestPermission(function (status) {
            if (Notification.permission !== status) {
                Notification.permission = status
            }
            if (status === 'granted') {
                var notification = new Notification(message, options)
                callback(null, notification)
            } else {
                callback(new Error('user denied'))
            }
        })
    } else {
        callback(new Error('doesn\'t support Notification API'))
    }
}

function notifyMessageAsPromise (message, options) {
    return new Promise(function (resolve, reject) {
        notifyMessage(message, options, function (error, notification) {
            if (error) {
                reject(error)
            } else {
                resolve(notification)
            }
        })
    })
}

// 运行示例
notifyMessageAsPromise('Hi!').then(function (notification) {
    console.log(notification) // 通知对象
}).catch(function(error){
    console.error(error)
})

采取thenable对象形式:

function notifyMessage (message, options, callback) {
    if (Notification && Notification.permission === 'granted') {
        var notification = new Notification(message, options)
        callback(null, notification)
    } else if (Notification.requestPermission) {
        Notification.requestPermission(function (status) {
            if (Notification.permission !== status) {
                Notification.permission = status
            }
            if (status === 'granted') {
                var notification = new Notification(message, options)
                callback(null, notification)
            } else {
                callback(new Error('user denied'))
            }
        })
    } else {
        callback(new Error('doesn\'t support Notification API'))
    }
}

// 返回 `thenable`
function notifyMessageAsThenable (message, options) {
    return {
        'then': function (resolve, reject) {
            notifyMessage(message, options, function (error, notification) {
                if (error) {
                    reject(error)
                } else {
                    resolve(notification)
                }
            })
        }
    }
}

// 运行示例
Promise.resolve(notifyMessageAsThenable('message')).then(function (notification) {
    console.log(notification) // 通知对象
}).catch(function (error) {
    console.error(error)
})

Thenable风格展现也位于回调和Promise风格中的一律栽状态,不用考虑Promise的兼容问题。一般不当作类库的明白API,更多状况下是于里面采用Thenable。Thenable对象还多的凡用来在Promise类库之间开展互动转换。

下thenable将promise对象转换为Q promise对象:

var Q = require('Q')

// 这是一个ES6的promise对象
var promise = new Promise(function (resolve) {
    resolve(1)
})

// 变换为Q promise对象
Q(promise).then(function (value) {
    console.log(value)
}).finally(function () { // Q promise对象可以使用finally方法
    console.log('finally')
})

Deferred和Promise

Deferred和Promise不同,它并未共通的业内,每个Library都是基于自己之喜欢好来落实之。

每当此间,我们打算为jQuery.Deferred类似之实现吗着力开展介绍。

概括来说,Deferred和Promise具有如下的涉及。

  • Deferred拥有Promis(当然为部分Deferred实现并没内涵Promise)
  • Deferred具备对Promise的状态进行操作的特权方法

用Deferred实现的getURL(Deferred基于promise实现):

function Deferred () {
    this.promise = new Promise(function (resolve, reject) {
        this._resolve = resolve
        this._reject = reject
    }.bind(this))
}

Deferred.prototype.resolve = function (value) {
    this._resolve.call(this.promise, value)
}

Deferred.prototype.reject = function (reason) {
    this._reject.call(this.promise, reason)
}

function getURL (URL) {
    var deferred = new Deferred()
    var req = new XMLHttpRequest()
    req.open('GET', URL, true)
    req.onload = function () {
        if (req.status === 200) {
            deferred.resolve(req.responseText)
        } else {
            deferred.reject(new Error(req.statusText))
        }
    }
    req.onerror = function () {
        deferred.reject(new Error(req.statusText))
    }
    req.send()
    return deferred.promise
}

// 运行示例
var URL = 'http://httpbin.org/get'
getURL(URL).then(function onFulfilled (value){
    console.log(value)
}).catch(console.error.bind(console))

Promise实现的getURL:

function getURL (URL) {
    return new Promise(function (resolve, reject) {
        var req = new XMLHttpRequest()
        req.open('GET', URL, true)
        req.onload = function () {
            if (req.status === 200) {
                resolve(req.responseText)
            } else {
                reject(new Error(req.statusText))
            }
        }
        req.onerror = function () {
            reject(new Error(req.statusText))
        }
        req.send()
    })
}

// 运行示例
var URL = 'http://httpbin.org/get'
getURL(URL).then(function onFulfilled (value){
    console.log(value)
}).catch(console.error.bind(console))

对比上述两单本子的getURL ,我们发现她来如下不同。

  • Deferred的说话不需要以代码用Promise括起来,由于并未于嵌套在函数中,可以减小一重叠缩进。
  • 转没有Promise里之错误处理逻辑。

当偏下地方,它们则好了千篇一律的劳作。

  • 整处理流程,调用resolve、reject的会。
  • 函数都归了promise对象。

是因为Deferred包含了Promise,所以大体的流水线或多的,不过Deferred有对Promise进行操作的特权方法,以及可以针对流程控制进行自由定制。

上面我们只是简单的贯彻了一个Deferred
,我思你早已看到了它们和Promise之间的距离了咔嚓。

苟说Promise是为此来对值进行抽象的讲话,Deferred则是指向处理还无收的状态或操作进行抽象化的目标,我们呢得以自当时同一叠的分别来解一下眼看两者之间的差距。

转换句话说,Promise代表了一个靶,这个目标的状态现在尚未确定,但是未来一个日子点它的状态要么改为正常值(FulFilled),要么改为异常值(Rejected);而Deferred对象表示了一个甩卖还尚无截止的这种事实,在它的处理终结之时段,可以通过Promise来取得处理结果。

使用Promise.race和delay取消XHR请求

XHR有一个timeout属性,使用该属性为足以概括实现超时功能,但是为了能支撑多单XHR同时超时或者其他力量,我们用了便于理解的异步方式在XHR中通过超时来促成取消在拓展中的操作。

1)让Promise等待指定时间

function delayPromise (ms) {
    return new Promise(function (resolve) {
        setTimeout(resolve, ms)
    })
}

delayPromise(100).then(function () {
    alert('已经过了100ms!')
})
  1. 利用promise.race()来落实超时promise:

    function timeoutPromise (promise, ms) { var timeout =
    delayPromise(ms).then(function () { throw new Error(‘Operation timed
    out after ‘ + ms + ‘ ms’) }) return Promise.race([promise,
    timeout]) }

点代码promise的状态改变之岁月越了ms就会throw Error。

// 运行示例
var taskPromise = new Promise(function(resolve){
    // 随便一些什么处理
    var delay = Math.random() * 2000
    setTimeout(function() {
        resolve(delay + 'ms')
    }, delay)
})

timeoutPromise(taskPromise, 1000).then(function (value) {
    console.log('taskPromise在规定时间内结束 : ' + value)
}).catch(function (error) {
    console.log('发生超时', error)
})

3)定制Error对象

为了能分这个Error对象的色,我们又来定义一个Error对象的子类TimeoutError。

function copyOwnFrom (target, source) {
    Object.getOwnPropertyNames(source).forEach(function (propName) {
        Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName))
    })
    return target
}

function TimeoutError () {
    var superInstance = Error.apply(null, arguments)
    copyOwnFrom(this, superInstance)
}

TimeoutError.prototype = Object.create(Error.prototype)
TimeoutError.prototype.constructor = TimeoutError

其的动办法以及通常的Error对象同,使用throw语句即可

var promise = new Promise(function () {
    throw new TimeoutError('timeout')
})

promise.catch(function (error) {
    console.log(error instanceof TimeoutError) // true
})

起了之TimeoutError对象,我们虽可知挺轻区分捕获的究竟是为超时而导致的荒谬,还是其它因致的Error对象了。

4)通过超时取消XHR操作

收回XHR操作自的话语并无麻烦,只需要调用XMLHttpRequest对象的abort()方法就好了。

为能够当外部调用abort()方法,我们事先对之前本节出现的getURL进行简单的壮大,cancelableXHR方法除了回到一个包装了XHR的promise对象之外,还回来了一个用于取消该XHR请求的abort方法。

function copyOwnFrom (target, source) {
    Object.getOwnPropertyNames(source).forEach(function (propName) {
        Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName))
    })
    return target
}

function TimeoutError () {
    var superInstance = Error.apply(null, arguments)
    copyOwnFrom(this, superInstance)
}

TimeoutError.prototype = Object.create(Error.prototype)
TimeoutError.prototype.constructor = TimeoutError

function delayPromise (ms) {
    return new Promise(function (resolve) {
        setTimeout(resolve, ms)
    })
}

function timeoutPromise(promise, ms) {
    var timeout = delayPromise(ms).then(function () {
            return Promise.reject(new TimeoutError('Operation timed out after ' + ms + ' ms'))
        })
    return Promise.race([promise, timeout])
}

function cancelableXHR(URL) {
    var req = new XMLHttpRequest()
    var promise = new Promise(function (resolve, reject) {
        req.open('GET', URL, true)
        req.onload = function () {
            if (req.status === 200) {
                resolve(req.responseText)
            } else {
                reject(new Error(req.statusText))
            }
        }
        req.onerror = function () {
            reject(new Error(req.statusText))
        }
        req.onabort = function () {
            reject(new Error('abort this request'))
        }
        req.send()
    })
    var abort = function () {
        // 如果request还没有结束的话就执行abort
        // https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
        if (req.readyState !== XMLHttpRequest.UNSENT) {
            req.abort()
        }
    }
    return {
        promise: promise,
        abort: abort
    }
}

var object = cancelableXHR('http://httpbin.org/get')

// main
timeoutPromise(object.promise, 1000).then(function (contents) {
    console.log('Contents', contents)
}).catch(function (error) {
    if (error instanceof TimeoutError) {
        object.abort()
        return console.log(error)
    }
    console.log('XHR Error :', error)
})

5)代码分割优化处理

在前头的cancelableXHR中,promise对象及其操作方法都是于一个对象吃归的,看起有点有来未极端好掌握。

于代码组织的角度来说一个函数只回去一个价(promise对象)是一个要命好的习惯,但是由于当外场不克看cancelableXHR方法中开创的req变量,所以我们要编制一个专程的函数(上面的例子中之abort)来对这些中对象进行拍卖。

理所当然也得考虑到对回的promise对象进行扩展,使该绷abort方法,但是由于promise对象是对值进行抽象化的目标,如果未加限制的增操作用的法门吧,会如整变得非常复杂。

世家还知情一个函数做最好多之办事还不认为是一个好之惯,因此我们无见面给一个函数完成具有力量,也许像下这样针对性函数进行分是一个没错的挑三拣四。

  • 返包含XHR的promise对象
  • 接纳promise对象作为参数并撤销该对象中之XHR请求

拿这些处理整理也一个模块的话,以后扩展起来呢造福,一个函数所召开的办事为会见较简略,代码也会见再便于看与维护。

运用common.js规范来写cancelableXHR.js:

'use strict'
var requestMap = {}

function createXHRPromise (URL) {
    var req = new XMLHttpRequest()
    var promise = new Promise(function (resolve, reject) {
        req.open('GET', URL, true)
        req.onreadystatechange = function () {
            if (req.readyState === XMLHttpRequest.DONE) {
                delete requestMap[URL]
            }
        }
        req.onload = function () {
            if (req.status === 200) {
                resolve(req.responseText)
            } else {
                reject(new Error(req.statusText))
            }
        }
        req.onerror = function () {
            reject(new Error(req.statusText))
        }
        req.onabort = function () {
            reject(new Error('abort this req'))
        }
        req.send()
    })
    requestMap[URL] = {
        promise: promise,
        request: req
    }
    return promise
}

function abortPromise (promise) {
    if (typeof promise === 'undefined') {
        return
    }
    var request
    Object.keys(requestMap).some(function (URL) {
        if (requestMap[URL].promise === promise) {
            request = requestMap[URL].request
            return true
        }
    })
    if (request != null && request.readyState !== XMLHttpRequest.UNSENT) {
        request.abort()
    }
}

module.exports.createXHRPromise = createXHRPromise
module.exports.abortPromise = abortPromise

调用:

var cancelableXHR = require('./cancelableXHR')

var xhrPromise = cancelableXHR.createXHRPromise('http://httpbin.org/get') // 创建包装了XHR的promise对象

xhrPromise.catch(function (error) {
    // 调用 abort 抛出的错误
})

cancelableXHR.abortPromise(xhrPromise) //   取消在创建的promise对象的请求操作

promise串行处理

Promise.all()可以拓展promise对象的并行处理,那么怎么落实串行处理啊?

俺们拿拍卖内容统一置于数组里,再配合for循环进行处理:

var request = {
    comment: function getComment () {
        return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse)
    },
    people: function getPeople () {
        return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse)
    }
}

function main() {
    function recordValue(results, value) {
        results.push(value)
        return results
    }

    // [] 用来保存初始化值
    var pushValue = recordValue.bind(null, [])

    // 返回promise对象的函数的数组
    var tasks = [request.comment, request.people]

    var promise = Promise.resolve()

    // 开始的地方
    for (var i = 0; i < tasks.length; i++) {
        var task = tasks[i]
        promise = promise.then(task).then(pushValue)
    }

    return promise
}

// 运行示例
main().then(function (value) {
    console.log(value)
}).catch(function (error) {
    console.error(error)
})

方代码中的promise =
promise.then(task).then(pushValue)通过持续对promise进行拍卖,不断的覆盖promise变量的价,以达成对promise对象的积处理功能。

然这种方式需要promise这个临时变量,从代码质量及的话显得不那么简单。我们得用Array.prototype.reduce来优化main函数:

function main() {

    function recordValue (results, value) {
        results.push(value)
        return results
    }

    var pushValue = recordValue.bind(null, [])
    var tasks = [request.comment, request.people]

    return tasks.reduce(function (promise, task) {
        return promise.then(task).then(pushValue)
    }, Promise.resolve())
}

实质上我们可提炼出进行依次处理的函数:

function sequenceTasks(tasks) {
    function recordValue(results, value) {
        results.push(value)
        return results
    }
    var pushValue = recordValue.bind(null, [])
    return tasks.reduce(function (promise, task) {
        return promise.then(task).then(pushValue)
    }, Promise.resolve())
}

这样我们如果如下调用,代码也越加清楚易掌握了:

var request = {
    comment: function getComment() {
        return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse)
    },
    people: function getPeople() {
        return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse)
    }
}

function main() {
    return sequenceTasks([request.comment, request.people])
}

// 运行示例
main().then(function (value) {
    console.log(value)
}).catch(function (error) {
    console.error(error)
})

又请按照顺序处理

下的内容来自google开发社区的一律篇有关promise的篇章JavaScript
Promise:简介

如我们只要因story.json通过ajax获取章节内容,每一样软ajax只能取得一节约内容。那么怎么就以急忙而能够随次展示章节内容为?即如果第一段下充斥了晚,我们而拿其上加到页面。这可于用户以另外章节下充斥了前先开始读。如果第三章节比第二章节先下充斥了后,我们无将那补偿加到页面,因为还少第二章节。第二章节下充斥了后,我们可续加第二章与老三章,后面章节也是如此丰富。

前同节约之串行方法才能够一个ajax请求task处理完晚更去实践下一个task,而Promise.all()能而且要,但是只有所有要了晚才能够得稳步的数组。

现实实现请圈下面实例。

俺们好行使JSON来又获有章节,然后创建一个望文档中上加章节的相继。

story.json如下:

{
    "heading": "<h1>A story about something</h1>",
    "chapterUrls": [
        "chapter-1.json",
        "chapter-2.json",
        "chapter-3.json",
        "chapter-4.json",
        "chapter-5.json"
    ]
}

现实处理代码:

function getJson(url) {
  return get(url).then(JSON.parse)
}

getJSON('story.json')
    .then(function (story) {
        addHtmlToPage(story.heading) // 文章头部添加到页面

        // 将拿到的chapterUrls数组map为json promises数组,这样可以保证并行下载
        return story.chapterUrls
            .map(getJSON)
            .reduce(function(sequence, chapterPromise) {
                // 用reduce方法链式调用promises,并将每个章节的内容到添加页面
                return sequence.then(function () {
                    // 等待获取当前准备插入页面的顺序的资源,然后等待这个顺序对应章节的成功请求
                    // Wait for everything in the sequence so far, then wait for this chapter to arrive.
                    return chapterPromise
                }).then(function(chapter) {
                    addHtmlToPage(chapter.html) // 将章节内容到添加页面
                })
            }, Promise.resolve())
    })
    .then(function() {
        addTextToPage('All done') // 页面添加All done文字
    })
    .catch(function(err) {
        // catch错误信息
        addTextToPage('Argh, broken: '' + err.message)
    })
    .then(function() {
        document.querySelector('.spinner').style.display = 'none' // 关闭加载提示
    })

Promise和链式调用

以Promise中公可将then和catch等方法连在一起写。这非常像DOM或者jQuery中之链式调用。

一般的点子链都通过返回this将多单主意串联起来。

那么怎么当匪更改都发采取了方法链编写的代码ECMAScript的标接口的前提下,如何当里以Promise进行双重写吗?

1)fs中之方法链

以Node.js中的fs为例。

此外,这里的例子我们再看得起代码的易理解性,因此打骨子里来说这例子可能并无到底极端实用。

有fs-method-chain.js:

'use strict'
var fs = require('fs')

function File() {
    this.lastValue = null
}

// Static method for File.prototype.read
File.read = function FileRead(filePath) {
    var file = new File()
    return file.read(filePath)
}

File.prototype.read = function (filePath) {
    this.lastValue = fs.readFileSync(filePath, 'utf-8')
    return this
}

File.prototype.transform = function (fn) {
    this.lastValue = fn.call(this, this.lastValue)
    return this
}

File.prototype.write = function (filePath) {
    this.lastValue = fs.writeFileSync(filePath, this.lastValue)
    return this
}

module.exports = File

调用:

var File = require('./fs-method-chain')
var inputFilePath = 'input.txt',
    outputFilePath = 'output.txt'
File.read(inputFilePath)
    .transform(function (content) {
        return '>>' + content
    })
    .write(outputFilePath)

2)基于Promise的fs方法链

下面我们虽以无转移才的方法链对外接口的前提下,采用Promise对中间贯彻进行重写。

'use strict'
var fs = require('fs')

function File() {
    this.promise = Promise.resolve()
}

// Static method for File.prototype.read
File.read = function (filePath) {
    var file = new File()
    return file.read(filePath)
}

File.prototype.then = function (onFulfilled, onRejected) {
    this.promise = this.promise.then(onFulfilled, onRejected)
    return this
}

File.prototype['catch'] = function (onRejected) {
    this.promise = this.promise.catch(onRejected)
    return this
}

File.prototype.read = function (filePath) {
    return this.then(function () {
        return fs.readFileSync(filePath, 'utf-8')
    })
}

File.prototype.transform = function (fn) {
    return this.then(fn)
}

File.prototype.write = function (filePath) {
    return this.then(function (data) {
        return fs.writeFileSync(filePath, data)
    })
}

module.exports = File

3)两者的别

设若说fs-method-chain.js和Promise版两者之间的差别,最充分之不比那就算使算一起跟异步了。

假使当看似fs-method-chain.js的法门链中加入队列等处理的话,就足以实现几乎与异步方法链同样的效用,但是落实将会转换得非常复杂,所以我们选了简单的一道方法链。

Promise版的言语似乎之前章节所说就会展开异步操作,因此下了promise的方法链也是异步的。

另外两者的错误处理方式为是勿均等的。

虽说fs-method-chain.js里面连无分包错误处理的逻辑,但是出于是同步操作,因此可将整段代码用try-catch包起来。

以Promise版提供了负为中promise对象的then和catch别名,所以我们可以像任何promise对象同采用catch来进展错误处理。

假设您想以fs-method-chain.js中温馨实现异步处理的话,错误处理可能会见成为比较深之题目;可以说当开展异步处理的时刻,还是用Promise实现起来比较简单。

4)Promise之外的异步处理

若你异常熟稔Node.js的話,那么看看方法链的话,你是勿是碰头怀念起来Stream呢。

苟用Stream的话,就得不去了封存this.lastValue的麻烦,还会改进处理非常文件时的属性。
另外,使用Stream的讲话也许会见比使用Promise在处理速度上会快些。

因而,在异步处理的时段并无是说Promise永远都是最好之挑选,要依据自己之目的与骨子里情形选择相当的落实方式。

5)Promise wrapper

又回来fs-method-chain.js和Promise版,这片栽艺术相较之中贯彻为酷相近,让人当是未是一同版本的代码可以一直就是当做异步方式来行使也?

由JavaScript可以为目标动态增长方法,所以打理论及吧应该好于非Promise版自动生成Promise版的代码。(当然静态定义的实现方式易处理)

尽管ES6
Promises并从未供者意义,但是著名的老三着Promise实现类似库bluebird等供了给喻为Promisification的意义。

只要应用类这样的类库,那么就算可动态为目标多promise版的方法。

var fs = Promise.promisifyAll(require('fs'))

fs.readFileAsync('myfile.js', 'utf8').then(function (contents) {
    console.log(contents)
}).catch(function (e) {
    console.error(e.stack)
})

前方的Promisification具体还干了若干什么光凭想象可能非绝好懂,我们可以通过让原生
Array增加Promise版的章程呢例来开展验证。

每当JavaScript炎黄生DOM或String等也供了广大创建方法链的成效。Array中即来像map和filter等措施,这些方法会返回一个数组类型,可以为此这些方式好之组建方法链。

'use strict'

function ArrayAsPromise (array) {
    this.array = array
    this.promise = Promise.resolve()
}

ArrayAsPromise.prototype.then = function (onFulfilled, onRejected) {
    this.promise = this.promise.then(onFulfilled, onRejected)
    return this
}

ArrayAsPromise.prototype['catch'] = function (onRejected) {
    this.promise = this.promise.catch(onRejected)
    return this
}

Object.getOwnPropertyNames(Array.prototype).forEach(function (methodName) {
    // Don't overwrite
    if (typeof ArrayAsPromise[methodName] !== 'undefined') {
        return
    }
    var arrayMethod = Array.prototype[methodName]
    if (typeof  arrayMethod !== 'function') {
        return
    }
    ArrayAsPromise.prototype[methodName] = function () {
        var that = this
        var args = arguments
        this.promise = this.promise.then(function () {
            that.array = Array.prototype[methodName].apply(that.array, args)
            return that.array
        })
        return this
    }
})

module.exports = ArrayAsPromise
module.exports.array = function newArrayAsPromise (array) {
    return new ArrayAsPromise(array)
}

原生的Array和ArrayAsPromise在行使时有什么异样也?我们好透过对上面的代码进行测试来打听她之间的不同点。

'use strict'
var assert = require('power-assert')
var ArrayAsPromise = require('../src/promise-chain/array-promise-chain')

describe('array-promise-chain', function () {
    function isEven(value) {
        return value % 2 === 0
    }

    function double(value) {
        return value * 2
    }

    beforeEach(function () {
        this.array = [1, 2, 3, 4, 5]
    })

    describe('Native array', function () {
        it('can method chain', function () {
            var result = this.array.filter(isEven).map(double)
            assert.deepEqual(result, [4, 8])
        })
    })

    describe('ArrayAsPromise', function () {
        it('can promise chain', function (done) {
            var array = new ArrayAsPromise(this.array)
            array.filter(isEven).map(double).then(function (value) {
                assert.deepEqual(value, [4, 8])
            }).then(done, done)
        })
    })
})

咱们看看,在ArrayAsPromise中也克采用Array的法。原生的Array是并处理,而ArrayAsPromise则是异步处理。

有心人看一下ArrayAsPromise的贯彻,也许你曾注意到了,Array.prototype的拥有办法还受实现了。但是,Array.prototype中呢设有着仿佛array.indexOf等并无见面回数组类型数据的主意,这些艺术而也要是支持链式调用的讲话虽稍微不自了。

每当这里很重要之一点凡,我们可以经过这种办法,为具备吸收相同档次数据接口的API动态的始建Promise版的API。如果我们会窥见及这种API的规则性的话语,那么即使可能发现部分初的采用办法。

友善实现一个Promise类

剖析Promise内部结构,一步一步实现一个一体化的、能由此有Test
case的Promise类

Promise反面模式(anti-pattern)

关于反面模式,维基百科是这般定义的:在软件工程被,一个反面模式(anti-pattern或antipattern)指的凡在实践中明显出现而以不行或是有待优化的设计模式,是因此来缓解问题的蕴藏共同性的不良方法。

Promise中泛的反面模式起嵌套的promise、没有正确error handle等。

Promise反面模式原文

Promise反面模式中文翻译

Promise常见错误

We have a problem with
promises原文

We have a problem with promises中文翻译

另强大的异步处理方式

1)使用async/await

async/await更加强有力,能写起再如一道的代码。但是基础依然是若控Promise。

2)使用Rxjs(Angular2后框架自带)。

参考

JavaScript
Promise迷你书(中文版)

阮一峰 ECMAScript6入门
Promise对象

JavaScript
Promise:简介