摘要:规范中对于构造函数没有明确说明,所以在此处拿出来讲解一下。构造函数只接收一个参数,且该参数必须是一个函数,任何其他的值比如等都会报一个的错误。
本篇文章是Promise系列文章的第二篇,主要是讲解基于Promise/A+规范,在传入不同类型的参数时,promise内部分别会如何处理。本章的主要目的是让大家对promise有一个更加深入的理解,也为下一篇讲如何实现一个promise库做准备。(写完之后觉得好水。。。)
英文版本的规范见这里,segmentfault上也有人把规范翻译为中文,见这里。
在此,我主要是通过使用例子,讲解一下规范中then方法和Promise Resolution Procedure的每一种情况。
constructor规范中对于构造函数没有明确说明,所以在此处拿出来讲解一下。
和普通JavaScript对象一样,我们同样是通过new关键词来创建一个Promise对象实例。构造函数只接收一个参数,且该参数必须是一个函数,任何其他的值比如undefined、null、5、true等都会报一个TypeError的错误。例:
new Promise(true) // Uncaught TypeError: Promise resolver true is not a function(…)
同样,如果你没有通过new关键词创建,而是直接执行Promise(),同样也会报一个TypeError的错误。
Promise() // Uncaught TypeError: undefined is not a promise(…)
所以,我们必须通过new Promise(function()=>{})的方式来创建一个Promise实例。通常我们见到的创建一个Promise实例的代码如下:
var promise = new Promise(function(resolve, reject) { // 进行一些异步操作 // 然后调用resolve或reject方法 });
这才是正确的姿势~ 从该例子中,我们可以看到创建Promise实例时传入的函数,同时还接受两个参数,它们分别对应Promise内部实现的两个方法。上一篇文章中,我提到过Promise有三种状态,pending、fulfilled、rejected,实例刚创建时处于pending状态,当执行reject方法时,变为rejected状态,如下所示:
new Promise(function(resolve, reject){ reject(Promise.resolve(5)) }).then(function(value){ console.log("fulfill", value) }, function(reason){ console.log("reject", reason) }) // reject Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 5}
而当执行resolve方法时,它可能变为fulfilled,也有可能变为rejected状态。也就是说resolve != fulfill。如下:
new Promise(function(resolve, reject){ resolve(Promise.reject(5)) }).then(function(value){ console.log("fulfill", value) }, function(reason){ console.log("reject", reason) }) // reject 5
那么resolve是个什么东西呢?它是根据什么变为fulfilled或rejected的呢?这就是我们接下来要讲解的Promise Resolution Procedure,我把它称作“Promise处理程序”。
Promise Resolution Procedure讲之前,我们先说几个promise规范中的几个术语。
promise 它是一个拥有then方法的对象或函数,且符合该规范
thenable 拥有then方法的对象或函数
value 是指一个合法的 Javascript 值
exception throw语句抛出的异常
reason 描述promise为什么失败的值
Promise Resolution Procedure是对传入的promise和value进行抽象操作。我们可一个把它理解成resolve(promise, value),对参数promise和value进行一系列处理操作。下面我们按照规范中的顺序,依次介绍每种情况。
2.3.1 如果promise和value指向同一个对象,则reject该promise并以一个TypeError作为reason。
var defer = {} var promise = new Promise(function(resolve){ defer.resolve = resolve }) promise.catch(function(reason){ console.log(reason) }) defer.resolve(promise) // TypeError: Chaining cycle detected for promise #(…)
我们把resolve函数保存在defer中,这样就可以在外部对promise进行状态改变,defer.resolve(promise)中的promise正是我们创建的对象,根据规范抛出了TypeError。
2.3.2 如果value是一个promise对象,且是基于当前实现创建的。
2.3.2.1 如果value处于pending状态,则promise同样pending并直到value状态改变。
2.3.2.2 如果value处于fulfilled状态,则使用相同的value值fulfill promise。
2.3.2.3 如果value处于rejected状态,则使用相同的reason值reject promise。
var promise1 = new Promise((resolve) => { setTimeout(() => { resolve(5) },3000) }); console.time("fulfill") var promise = new Promise((resolve) => { resolve(promise1) }) promise.then((value) => { console.timeEnd("fulfill") console.log("fulfill", value) }) setTimeout(()=>{ console.log("setTimeout", promise) }, 1000) // setTimeout Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined} // fulfill: 3.01e+03ms // fulfill 5
通过该例子可以看出,最后setTimeout延迟1秒查看promise状态时,它依然处于pending状态,当3秒后promise1变为fulfilled后,promise随即变为fulfilled并以5作为value传给then添加的成功回调函数中。
var promise1 = new Promise((resolve, reject) => { setTimeout(() => { reject(new Error("error")) }, 3000) }); console.time("reject") var promise = new Promise((resolve) => { resolve(promise1) }) promise.catch((reason) => { console.timeEnd("reject") console.log("reject", reason) }) setTimeout(()=>{ console.log("setTimeout", promise) }, 1000) // setTimeout Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined} // reject: 3e+03ms // reject Error: error(…)
失败时例子与成功时类似。
2.3.3 如果value是一个对象或函数
2.3.3.1 使then等于value.then
2.3.3.2 如果获取value.then的值时抛出异常,这通过该异常reject promise,例:
new Promise((resolve)=>{ resolve({then:(()=>{ throw new Error("error") })() }) }).catch((reason)=>{ console.log(reason) }) // Error: error(…)
上例中获取value.then时,会抛出异常
2.3.3.3 如果then是一个函数,则把value作为函数中this指向来调用它,第一个参数是resolvePromise,第二个参数是rejectPromise。
其实这里主要是为了兼容两种情况,第一种是传入的value是个Deferred对象,则状态和Deferred对象一致;另一种情况是不是使用当前构造函数创建的Promise对象,通过这种方式可以兼容,达到一致的效果。
2.3.3.3.1 如果resolvePromise通过传入y来调用,则执行resolve(promise, y),例:
new Promise((resolve)=>{ resolve({then:(resolvePromise, rejectPromise)=>{ resolvePromise(5) } }) }).then((value)=>{ console.log(value) }) // 5
2.3.3.3.2 如果rejectPromise 通过传入原因r来调用,则传入r来reject promise,例:
new Promise((resolve)=>{ resolve({then:(resolvePromise, rejectPromise)=>{ rejectPromise(new Error("error")) } }) }).catch((reason)=>{ console.log(reason) }) // Error: error(…)
2.3.3.3.3 如果resolvePromise和rejectPromise都被调用,或其中一个被调用了多次,则以第一次调用的为准,并忽略之后的调用。例:
new Promise((resolve)=>{ resolve({then:(resolvePromise, rejectPromise)=>{ resolvePromise(5) rejectPromise(new Error("error")) } }) }).then((value)=>{ console.log(value) }, (reason)=>{ console.log(reason) }) // 5
2.3.3.3.4 如果调用then抛出异常e:
2.3.3.3.4.1 如果resolvePromise或rejectPromise已经调用,则忽略它,例:
new Promise((resolve)=>{ resolve({then:(resolvePromise, rejectPromise)=>{ resolvePromise(5) throw new Error("error") } }) }).then((value)=>{ console.log(value) }, (reason)=>{ console.log(reason) }) // 5
2.3.3.3.4.2 否则,则传入e来reject promise,例:
new Promise((resolve)=>{ resolve({then:(resolvePromise, rejectPromise)=>{ throw new Error("error") } }) }).then((value)=>{ console.log(value) }, (reason)=>{ console.log(reason) }) // Error: error(…)
2.3.3.4 如果then不是一个函数,则传入value来fulfill promise,例:
new Promise((resolve)=>{ resolve({then:5}) }).then((value)=>{ console.log(value) }, (reason)=>{ console.log(reason) }) // Object {then: 5}then 方法
一个promise必须提供一个then方法来处理成功或失败。
then方法接收两个参数:
promise.then(onFulfilled, onRejected)
2.2.1 onFulfilled和onRejected都是可选的
2.2.1.1 如果onFulfilled不是一个函数,则忽略。例:
Promise.resolve(5) .then(true,function(reason){ console.log(reason) }) .then(function(value){ console.log(value) }) // 5
2.2.1.2 如果onRejected不是一个函数,则忽略。例:
Promise.reject(new Error("error")) .then(true,null) .then(undefined,function(reason){ console.log(reason) }) // Error: error(…)
2.2.2 如果onFulfilled是一个函数
2.2.2.1 它必须在promise变为fulfilled之后调用,且把promise的value作为它的第一个参数
这个从我们所有的例子中都可以看出
2.2.2.2 它不可以在promise变为fulfilled之前调用
var defer = {} console.time("fulfill") var promise = new Promise((resolve)=>{ defer.resolve = resolve }); promise.then((value)=>{ console.timeEnd("fulfill") }) setTimeout(()=>{ defer.resolve(5) },1000); // fulfill: 1e+03ms
从onFulfilled执行的时间可以看出promise直到变为fulfilled后才调用
2.2.2.3 它只可以被调用一次
var defer = {} var promise = new Promise((resolve)=>{ defer.resolve = resolve }); promise.then((value)=>{ console.log(value++) }) defer.resolve(5) // 5 defer.resolve(6) // 后面不再次执行
2.2.3 如果onRejected是一个函数
2.2.3.1 它必须在promise变为rejected之后调用,且把promise的reason作为它的第一个参数
2.2.3.2 它不可以在promise变为rejected之前调用
2.2.3.3 它只可以被调用一次
onRejected和onFulfilled基本类似,这里不再次赘述
2.2.4 onFulfilled和onRejected是在执行环境中仅包含平台代码时调用
这里有一个备注,平台代码是指引擎、执行环境、以及promise的实现代码。实际过程中,要确保onFulfilled和onRejected是异步执行的,它是在event loop过程中then方法被调用之后的新调用栈中执行。我们可以使用setTimeout或setImmediate等macro-task机制来实现,也可以使用MutationObserver或process.nextTick等micro-task机制来实现。promise的实现本身就被看作是平台代码,它本身就包含一个处理器可以调用的任务调度队列。
才疏学浅,没理解它这一条到底要表达一个什么意思。。。应该指的就是异步执行,因为异步执行的时候,页面中同步的逻辑都已经执行完毕,所以只剩下平台代码。
注:原生的Promise实现属于micro-task机制。macro-task和micro-task分别是两种异步任务,它们的不同后面会多带带讲一下。下面列出了常见的异步方法都属于那种异步机制:
macro-task: script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering
micro-task: process.nextTick, 原生Promise, Object.observe, MutationObserver
2.2.5 onFulfilled和onRejected必须作为函数来调用,没有this值
Promise.resolve(5).then(function(){ console.log(this) }) // Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, localStorage: Storage, sessionStorage: Storage, webkitStorageInfo: DeprecatedStorageInfo…}
2.2.6 同一个promise上的then方法可能会调用多次
2.2.6.1 如果promise fulfilled,则所有的onFulfilled回调函数按照它们添加的顺序依次调用。
var defer = {} var promise = new Promise((resolve)=>{ defer.resolve = resolve }); promise.then((value)=>{ console.log(1,value++) }) promise.then((value)=>{ console.log(2,value++) }) promise.then((value)=>{ console.log(3,value++) }) defer.resolve(5) // 1 5 // 2 5 // 3 5
2.2.6.2 如果promise rejected,则所有的onRejected回调函数按照它们添加的顺序依次调用。
例子与上例类似
2.2.7 then方法会返回一个全新的promise
promise2 = promise1.then(onFulfilled, onRejected);
2.2.7.1 如果onFulfilled或onRejected返回了一个值x,则执行resolve(promise2, x)
Promise.resolve(5).then(function(value){ return ++value }).then(function(value){ console.log(value) }) // 6
2.2.7.2 如果onFulfilled或onRejected抛出了异常e,则reject promise2并传入原因e
Promise.resolve(5).then(function(value){ throw new Error("error") }).catch(function(reason){ console.log(reason) }) // Error: error(…)
2.2.7.3 如果onFulfilled不是一个函数且promise1 fulfilled,则promise2以同样的value fulfill
Promise.resolve(5).then("tiaoguo").then(function(value){ console.log(value) }) // 5
2.2.7.4 如果onRejected不是一个函数且promise1 rejected,则promise2以同样的reason reject
Promise.reject(new Error("error")).catch("tiaoguo").catch(function(reason){ console.log(reason) }) // Error: error(…)
更多的测试代码,大家可以去promises-tests中查看,这是一个基于规范的promise测试库。
以上基本是整个Promise/A+规范的所有内容,如有错误,欢迎批评指正。下一篇我会根据规范一步一步实现一个Promise polyfill库。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/88042.html
摘要:我们称为回调对象,它内部会维护一个数组,我们可以向其中添加若干个回调函数,然后在某一条件下触发执行。第一次之后,再次新的回调函数时,自动执行回调。当前面的回调函数返回时,终止后面的回调继续执行。 最近懒癌发作,说好的系列文章,写了一半,一直懒得写,今天补上一篇。 Deferred 我们在使用promise对象时,总会提到一个与它关系密切的对象——Deferred。其实Deferred没...
摘要:的翻译文档由的维护很多人说,阮老师已经有一本关于的书了入门,觉得看看这本书就足够了。前端的异步解决方案之和异步编程模式在前端开发过程中,显得越来越重要。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。 JavaScript Promise 迷你书(中文版) 超详细介绍promise的gitbook,看完再不会promise...... 本书的目的是以目前还在制定中的ECMASc...
摘要:内部总体上分为两种情况,一种是当前对象状态已经变为或,此时则直接把响应的回调函数添加到异步队列中,另一种情况是当前对象状态还是,此时则把响应的回调函数依次添加到数组中。 今天,我带着大家一步一步跟着规范实现一个自己的Promise,大家可以对照我的第二篇文章Promise介绍--规范篇或官方规范来一一学习。 Promise内部有三个固定的状态,我们在文件中提前定义。 const PEN...
摘要:因此,当作为参数的执行任意结果的回调函数时,就会将参数传递给外层的,执行对应的回调函数。 背景 在上一篇博客[[译]前端基础知识储备——Promise/A+规范](https://segmentfault.com/a/11...,我们介绍了Promise/A+规范的具体条目。在本文中,我们来选择了promiz,让大家来看下一个具体的Promise库的内部代码是如何运作的。 promiz...
摘要:事件循环持续运行,直到清空列队的任务。在执行期间,浏览器可能更新渲染。线索可能会发生多次。由于冒泡,函数再一次执行。这意味着队列不会在事件回调之间处理,而是在它们之后处理。当触发成功事件时,相关的对象在事件之后转为非激活状态第四步。 一 前言 一直想对异步处理做一个研究,在查阅资料时发现了这篇文章,非常深入的解释了事件循环中重的任务队列。原文中有代码执行工具,强烈建议自己执行一下查看结...
阅读 1695·2021-11-24 09:39
阅读 2466·2021-11-18 10:07
阅读 3655·2021-08-31 09:40
阅读 3316·2019-08-30 15:44
阅读 2627·2019-08-30 12:50
阅读 3648·2019-08-26 17:04
阅读 1428·2019-08-26 13:49
阅读 1261·2019-08-23 18:05