Promise小书(长文)

此外强大的异步处理情势

1)使用async/await

async/await更抓牢劲,能写出更像一块的代码。不过基础依旧是要控制Promise。

二)使用Tiguanxjs(Angular贰后框架自带)。

promise chain中怎么着传递参数?

倘诺Task A想给Task
B传递八个参数该怎么做吧?其实十分简单,只要在taskA中return2个值,那一个值会作为参数字传送递给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对象唯有两种情形。

  • 异步操作“未到位”,promise对象刚被创立后的开首化状态(unresolved,Promises/A+中称pending)
  • 异步操作“已到位”(resolved,Promises/A+中称fulfilled)
  • 异步操作“失利”(rejected)

那二种的图景的生成路径唯有三种。

  • 异步操作从“未成功”到“已形成”
  • 异步操作从“未成功”到“败北”。

那种变动只好发出2次,1旦当前情况成为“已做到”或“战败”,就表示不会再有新的景色变化了。因而,Promise对象的最终结果唯有三种。

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

异步操作战败,Promise对象抛出1个荒唐,状态成为rejected。

前言

本文重要参照了JavaScript PromiseMini书,链接在文末与任何参考壹起列出。

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时的回调函数

一)taskA、taskB都并未有发出分外,会安份守己taskA → taskB →
finalTask这些流程来实行拍卖

二)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方法故意创造了2个不胜。但在事实上中想再接再砺展开onRejected调用的时候,应该回到三个Rejected状态的promise对象。

创建promise对象

给Promise构造函数字传送递3个函数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)
        })

getULacrosseL只有在经过XH汉兰达取得结果景况为200时才会调用resolve。约等于唯有数量得到成功时,而别的意况(取得失败)时则会调用reject方法。

resolve(req.responseText)在response的内容中进入了参数。resolve方法的参数并不曾专门的条条框框,基本上把要传给回调函数参数放进去就能够了。(then方法可以吸收接纳到那几个参数值)

IE8及IE8以下catch包容难点

IE八及IE捌以下正是已经引入了Promise的polyfill,使用catch方法依旧会冒出identifier
not found的语法错误。

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

在ECMAScript
叁中保留字是无法看做对象的属性名使用的。而IE八及以下版本都以基于ECMAScript
三落到实处的,由此不能够将catch作为质量来使用,也就不能够编写类似promise.catch()的代码,由此就涌出了identifier
not found那种语法错误了。

而现代浏览器都匡助ECMAScript 5,而在ECMAScript
5中保留字都属于IdentifierName,也得以当做属性名使用了。

点标记法(dot notation)要求对象的习性必须是有效的标识符(在ECMAScript
三中则不能使用保留字)。

但是使用中括号标记法(bracket
notation)的话,则足以将非合法标识符作为靶子的属性名使用。

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

鉴于catch标识符也许会招致难题出现,因而有个别类库(Library)也选取了caught作为函数名,而函数要完结的工作是相同的。

并且不少压缩工具自带了将promise.catch转换为promise[‘catch’]的机能,所以恐怕相当的大心之间也能帮大家化解那个难题。

Promise常见错误

We have a problem with
promises原文

We have a problem with promises中文翻译

为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)

七个有效的叠加措施

1)done()

Promise对象的回调链,不管以then方法或catch方法结尾,假若最后二个艺术抛出荒唐,都有望不可能捕捉到(因为Promise内部的谬误不会冒泡到全局)。因而,大家能够提供3个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中抛出的丰裕不会被抓获,下边例子中的例外不会被抓走。

ES6Promises和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。笔者1般采纳那一个库promise.prototype.finally

并且伸手按序处理

上面包车型大巴剧情来自google开发社区的壹篇关于promise的小说JavaScript
Promise:简介

借使大家要依照story.json通过ajax获取章节内容,每1回ajax只好获取壹节内容。那么怎么达成又快又能按序呈现章节内容呢?即只要第二章下载完后,大家可将其添加到页面。这可让用户在别的章节下载达成前先起来读书。如若第一章比第一章先下载完后,我们不将其添加到页面,因为还不够第贰章。第2章下载完后,大家可添加第3章和第二章,后边章节也是那般充裕。

前壹节的串行方法只好二个ajax请求task处理完后再去实践下1个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.all

Promise.all方法用于将八个Promise实例,包装成二个新的Promise实例。

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

地点代码中,Promise.all方法接受三个数组作为参数,p壹、p二、p三都是Promise实例,假若不是,就会先调用Promise.resolve方法,将参数转为Promise实例,再进一步处理。

p的情状由p一、p二、p3决定,分成三种情景。

(1)唯有p壹、p二、p三的情景都成为resolved,p的情景才会化为resolved,此时p一、p二、p3的重临值组成一个数组,传递给p的回调函数。

(二)只要p壹、p二、p三之中有3个被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并不是3个个的逐条执行的,而是同时启幕、并行执行的。

比方那几个promise全体串行处理的话,那么必要等待一ms → 等待3二ms → 等待6四ms →
等待12八ms ,全部推行完结供给约225ms的年华。

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())
}

实际大家能够提炼出进行逐1处理的函数:

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)
})

promise基础

Promise是异步编制程序的一种缓解方案。ES6Promise的正儿八经来源于Promises/A+社区,它有不可枚举本子的兑现。

Promise比古板的化解方案(回调函数和事件)更合理和更加强有力,能够幸免回调鬼世界。使用Promise来归并处理异步操作,更具语义化、易于精晓、有利维护。

Promise接口的中坚思虑是让异步操作重临2个Promise对象,我们能够对这么些目的开始展览局地操作。

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))

比较上述五个本子的getULacrosseL ,大家发现它们有如下不一致。

  • Deferred的话不要求将代码用Promise括起来,由于尚未被嵌套在函数中,能够减去1层缩进。
  • 转头未有Promise里的错误处理逻辑。

在偏下地方,它们则完结了壹样的劳作。

  • 全部处理流程,调用resolve、reject的空子。
  • 函数都回去了promise对象。

出于Deferred包括了Promise,所以大体的流程仍然基本上的,可是Deferred有对Promise进行操作的特权方法,以及能够对流程控制实行随机定制。

下面大家只是简短的落到实处了二个Deferred
,作者想你已经阅览了它和Promise之间的差别了吧。

假使说Promise是用来对值实行抽象的话,Deferred则是对拍卖还尚未实现的景色或操作进行抽象化的对象,大家也得以从那一层的分别来了然一下那两者之间的出入。

换句话说,Promise代表了二个目的,这么些指标的地方今后还不鲜明,不过以往七个时日点它的气象要么改为不奇怪值(FulFilled),要么改为极度值(Rejected);而Deferred对象表示了一个处理还从未落成的那种事实,在它的拍卖完成的时候,可以经过Promise来得四处理结果。

Promise反面格局(anti-pattern)

关于反面方式,维基百科是这么定义的:在软件工程中,3个反面形式(anti-pattern或antipattern)指的是在实践中分明出现但又不行或是有待优化的设计格局,是用来缓解难题的包罗共同性的不佳方法。

Promise中普遍的反面情势有嵌套的promise、没有科学error handle等。

Promise反面格局原来的书文

Promise反面格局普通话翻译

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

执行上边的代码,会挨个输出一,二,叁。首先new
Promise中的函数会立时施行,然后是外围的console.log(2),最终是then回调中的函数。

出于promise.then执行的时候promise对象已经是规定状态,从程序上说对回调函数实行协同调用也是实惠的。

唯独就算在调用promise.then注册回调函数的时候promise对象已经是规定的情形,Promise也会以异步的章程调用该回调函数,那是在Promise设计上的鲜明方针。为啥要如此吗?

那关乎到1块调用和异步调用同时设有导致的混乱。

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
的第四7项不要对异步回调函数举行联合调用中也有详细介绍:

  • 相对不可能对异步回调函数(固然在数据已经就绪)进行联合调用。
  • 只要对异步回调函数举行同步调用的话,处理顺序恐怕会与预期不符,恐怕带来出人意料的结果。
  • 对异步回调函数进行共同调用,还只怕导致栈溢出或特别处理错乱等难点。
  • 壹旦想在明日某时刻调用异步回调函数的话,能够动用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 chain(Promise方法链)

Promise的兑现类库(Library)

由于过多浏览器不匡助ES6Promises,大家要求有的第贰方完成的和Promise兼容的类库。

慎选Promise类库首先要思虑的是还是不是具有Promises/A+包容性。

Promises/A+是ES六 Promises的前身,Promise的then也是由社区的正规化而来。

这么些类库重要有三种:Polyfill和扩充类库

1)Polyfill

贰)Promise扩张类库

  • kriskowal/q:
    Q.promise,那个大家应该都比较熟谙了。Angularjs中的$q也是受此启发。
  • petkaantonov/bluebird:那几个类库除了包容Promise规范之外,还扩张了收回promise对象的周转,取得promise的运行速度,以及错误处理的扩大检验等卓殊足够的效果,别的它在贯彻上还在性质难点下了相当大的造诣。

Q等文书档案里详细介绍了Q的Deferred和jQuery里的Deferred有如何异同,以及要怎么实行搬迁等都进展了详实的印证。

参考

JavaScript
Promise迷你书(中文版)

阮一峰 ECMAScript6入门
Promise对象

JavaScript
Promise:简介

团结实现三个Promise类

剖析Promise内部结构,一步一步实现一个完好无缺的、能因此装有Test
case的Promise类

再议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')
})

每回调用then都会回到2个新创设的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
})

下边是一个由艺术第11中学的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,他们都会接收1组promise对象为参数,并再次来到三个和收取参数分裂的、新的promise对象。

使用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()章节的四个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,肆-一,1,四-二,3(测试发现chrome55、5陆中则是初始打字与印刷出叁)。这些不明了怎么解释了,为何p三会最终执行?暂且没找到什么保险的材质,有大神知道的话,请评论提议。

Promise.reject()

Promise.reject(error)是和Promise.resolve(value)类似的静态方法,是new
Promise()方法的火速格局。

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

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

Promise和链式调用

在Promise中您可以将then和catch等方法连在一起写。那可怜像DOM恐怕jQuery中的链式调用。

一般的不二等秘书籍链都通过再次来到this将多少个章程串联起来。

这正是说怎么在不更改已有选取了方法链编写的代码的外表接口的前提下,怎么着在其间选用Promise实行重写吗?

一)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版两者之间的差异,最大的不等那就要算是1起和异步了。

借使在看似fs-method-chain.js的方法链中加入队列等拍卖的话,就能够完毕大概和异步方法链同样的效应,不过完毕将会变得非凡复杂,所以咱们选择了简便易行的一路方法链。

Promise版的话就好像在此以前章节所说只会开始展览异步操作,因而选取了promise的方法链也是异步的。

除此以外两者的错误处理方式也是不平等的。

即使如此fs-method-chain.js里面并不包蕴错误处理的逻辑,然则出于是同步操作,因而能够将整段代码用try-catch包起来。

在Promise版提供了指向当中promise对象的then和catch外号,所以大家能够像任何promise对象1样使用catch来举行错误处理。

即使您想在fs-method-chain.js中本身达成异步处理的话,错误处理或然会变成相比较大的标题;可以说在展开异步处理的时候,如故利用Promise落成起来相比较简单。

四)Promise之外的异步处理

若是你很熟识Node.js的話,那么看看方法链的话,你是否会想起来Stream呢。

假诺选用Stream的话,就足以防去了保留this.lastValue的难为,还可以够革新处理大文件时候的性质。
其余,使用Stream的话恐怕会比使用Promise在处理速度上会快些。

故而,在异步处理的时候并不是说Promise永远都以最棒的选拔,要基于本人的目标和实际情状选拔适用的贯彻格局。

5)Promise wrapper

再回去fs-method-chain.js和Promise版,那两种方式相比较之中贯彻也卓殊接近,令人认为是还是不是三头版本的代码能够平素就当作异步形式来行使呢?

是因为JavaScript可以向指标动态增加方法,所以从理论上的话应该能够从非Promise版自动生成Promise版的代码。(当然静态定义的贯彻格局简单处理)

固然ES6Promises并不曾提供此意义,可是有名的第二方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等格局,那个方法会重返1个数组类型,能够用那么些主意方便的组装方法链。

'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等并不会回到数组类型数据的方法,那几个方式假使也要辅助链式调用的话就稍微不自然了。

在那边11分主要的少数是,我们得以经过那种艺术,为保有吸收相同连串数据接口的API动态的创办Promise版的API。假如我们能窥见到那种API的规则性的话,那么就大概发现有个别新的运用方法。

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

XHR有一个timeout属性,使用该属性也得以简不难单完成超时功效,可是为了能支撑三个XHRubicon同时超时可能别的功能,大家使用了便于通晓的异步方式在XH劲客中通过超时来落到实处撤消正在展开中的操作。

一)让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对象的品种,我们再来定义3个Error对象的子类提姆eoutError。

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对象1样,使用throw语句即可

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

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

有了这一个TimeoutError对象,我们就能很不难区分捕获的到底是因为超时而导致的错误,依旧别的原因造成的Error对象了。

四)通过超时撤除XH本田UR-V操作

撤消XH昂科雷操作自个儿的话并不难,只需求调用XMLHttpRequest对象的abort()方法就能够了。

为了能在外表调用abort()方法,我们先对从前本节出现的getU卡宴L实行简要的扩张,cancelableXH昂科雷方法除了回到一个卷入了XH劲客的promise对象之外,还回来了一个用以废除该XHLAND请求的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)代码分割优化处理

在眼下的cancelableXH哈弗中,promise对象及其操作方法都以在3个目的中回到的,看起来有点有些不太好明白。

从代码协会的角度来说贰个函数只回去一个值(promise对象)是三个丰富好的习惯,不过由于在外边不可能访问cancelableXHEscort方法中创建的req变量,所以大家须求编写制定二个专门的函数(上边的例子中的abort)来对这几个内部对象进行拍卖。

理所当然也足以设想到对回到的promise对象开始展览扩展,使其帮忙abort方法,不过由于promise对象是对值进行抽象化的目标,尽管不加限制的充实际操作作用的方法的话,会使全部变得至极复杂。

我们都明白多少个函数做太多的劳作都不觉得是三个好的习惯,由此大家不会让贰个函数完毕全体功用,只怕像上边这样对函数举行剪切是3个不利的选择。

  • 回去包罗XH君越的promise对象
  • 收下promise对象作为参数并撤回该对象中的XH汉兰达请求

将这几个处理整理为2个模块的话,未来扩大起来也有利于,二个函数所做的干活也会比较简单,代码也会更易于阅读和掩护。

使用common.js规范来写cancelableXH奇骏.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对象的请求操作

api简介

此时此刻任重(Ren Zhong)而道远有三类别型

  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.race

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

与Promise.all类似,但是假若p壹、p②、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在第3个promise对象变为Fulfilled之后,并不会废除别的promise对象的履行。

在ES6Promises规范中,也从未裁撤(中断)promise对象实施的概念,我们不可能不要确认保证promise最后进入resolve
or
reject状态之1。也正是说Promise并不适用于状态恐怕会稳定不变的拍卖。也有局地类库提供了对promise进行撤除的操作。

Promise.resolve

1)new Promise的快速格局

静态方法Promise.resolve(value)能够认为是new
Promise()方法的连忙格局。Promise.resolve(value)重回四个动静由给定value决定的Promise对象。如若该值是二个Promise对象,则一贯回到该对象;即使该值是thenable对象(见上边部分二),再次来到的Promise对象的最后状态由then方法执行决定;不然的话(该value为空,基本项目也许不带then方法的指标),再次来到的Promise对象情况为resolved,并且将该value传递给相应的then方法。

就此和new
Promise()方法并不完全壹致。Promise.resolve接收二个promise对象会直接重返那些指标。而new
Promise()总是新生成2个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)
})

贰)Promise.resolve方法另二个效益正是将thenable对象转换为promise对象。

哪些是thenable对象?Thenable对象足以认为是类Promise对象,拥有名字为.then方法的目的。和类数组的概念一般。

有怎样thenable对象?首假如ES陆之前有诸多库完结了Promise,在那之中有过多与ES陆Promise规范并不雷同,大家称那么些与ES陆中的promise对象类似而又有异样的靶子为thenable对象。如jQuery中的ajax()方法再次来到的指标。

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

选用then的第2个参数依旧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则是1个能充足好的进展错误处理的本子。

怎么说badMain糟糕呢?,因为尽管大家在.then的第1个参数中内定了用来错误处理的函数,但事实上它却不可能捕获第1个参数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中是捕获不到这么些充裕的。

二)在promise.then(onResolved).catch(onRejected)的状态下

then中生出的不行能在.catch中抓获

叁).then和.catch在真相上是从未分其他

亟需分场地使用。

我们要求小心就算代码类似badMain那样的话,就可能出现程序不会按预期运维的动静,从而不可能正确的展开错误处理。

如何截止promise chain

在选择Promise处理部分复杂逻辑的进度中,大家有时候会想要在发出某种错误后就止住执行Promise链前边全体的代码。

但是Promise本人并未提供那样的职能,几个操作,要么成功,要么战败,要么跳转到then里,要么跳转到catch里。

具体怎么做,请查看那篇文章从哪些停掉 Promise
链提起