基础系列-promise

Promise的目的

人类大脑的习惯是线性的、阻塞的、单线程的语义。

回调表达异步流程的方式是非线性的、非顺序的,导致代码难以理解

回调会收到控制反转的影响,因为回调暗中会把控制权交给第三方。如只有第三方写了执行回调,才能执行回调。

避免“回调地狱”,消除多层嵌套,避免竞态条件出现。

JavaScript异常变成异步行为,进而极大降低了竞态条件出现的可能。

处理多个异步请求并发,需要同步处理多个异步请求结果。

promise 作用

Promise规范化了异步,并封装了时间相关值的状态,使得我们能够把他们以这种有用的方式链接到一起

含义:

  1. promise对象用于异步计算
  2. 一种在异步任务中作为两个或更多步骤的流程控制机制,时序上的this-then-that
  3. 包含两种状态,初始状态(pending)和已决议状态(fulfill和reject)
  4. promise只有一个决议值(完成或拒绝),决议后的状态不会改变

按照用途解释:

  1. 主要用于异步计算,
  2. 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果,
  3. 可以在对象之间传递和操作Promise,帮助我们处理队列。

promise 使用

初始化promise对象

new Promise(
	/* 执行器 executor */
    function (resolve, reject) {
        // 异步操作
        
        resolve(); // 数据处理完成
        
        reject(); // 数据处理出错
    }
).then(function A() {
    // 成功,resolve(),下一步
}, function B() {
    // 失败,reject(),作相应的处理
})

new Promise(function() {…})模式通常成为revealing constructor。传入的函数会立即执行(或同步的,不会像then(…)中的回调一样异步延迟),有两个参数是promise结果函数,resolve(…)通常标识完成,而reject(…)则标识拒绝。

Promise是一个代理对象,它和原先要进行的操作并无关系。

通过引入一个回调,避免更多的回调。

Promise的状态

  1. pending: (待定)初始状态

  2. fulfilled(resolve): (实现)操作成功

  3. rejected: (被否决)操作失败

Promise状态发生改变,就会触发.then()里的响应函数处理后续步骤。

Promise状态一经改变就不会再发生改变,一直保持结果不变,可以按照需求多次查看。

由于Promise只能被决议一次,所以任何通过then(...)注册的(每个)回调就只会被调用一次。

执行步骤:new Promise(executer)声明实例 -> executor执行器 -> .then()下一步…

例如:

// 示例1
new Promise(resolve => {
    setTimeout( () => {
        resolve('hello');
    }, 2000);
})
    .then( value => {
    	console.log(value + ' world');
});
// 输出 hello world

// 示例2
new Promise( resolve => {
    setTimeout( () => {
        resolve('hello');
    }, 2000);
})
    .then( value => {
        console.log(value);
        return new Promise( resolve => {
            setTimeout( () => {
                resolve('world')
            }, 2000);
        });
	})
    .then( value => {
    	console.log( value + ' world');
	})
// 输出:
// hello
// world world

// 示例3
new Promise( resolve => {
    setTimeout( () => {
        resolve('hello');
    }, 2000);
})
    .then( value => {
        console.log(value);
    	console.log('everyone');
        (function () {
            return new Promise(resolve => {
                setTimeout(() => {
                    console.log('new promise');
                    resolve('new peomise resolve');
                }, 2000);
            });
        }());
        return false;
	})
    .then( value => {
    	console.log( value + ' world');
	})
// 输出:
// hello
// everyone
// false world
// new promise

在promise中如果不直接返回新的promise对象,会顺序执行.then中的方法。

.then()

(1) .then()受两个函数作为参数,分别代表fulfilledrejected

(2) .then()返回一个新的Promise实例,所以它可以链式调用

(3) 当前面的Promise状态改变时,.then()据其最终状态,选择特定的状态响应函数执行

(4) 状态响应函数可以返回新的Promise,或其它值

(5) 如果返回新的Promise,那么下一级.then()会在新Promise状态改变之后执行

(6) 如果返回其它任何值,则会立刻执行下一级.then()

注意:当.then()里有.then()的情况时,因为.then()返回的还是Promise实例,会等里面的.then()执行完,再执行外面的.then()。不推荐这种写法,最好是将里面的.then()和外面的.then()放在同一级链式写法,因为当返回Promise实例时,只有当promise执行完成后才会执行.then().

如果使用多个参数调用resolve(...)或者reject(...),第一个参数之后的所有参数都会被默认忽略。如果要传递多个值,就必须要把值封装在单个值中传递,如数组或对象。默认传值参数为undefined

错误处理

Promise会自动捕获内部异常,并交给rejected响应函数处理。

new Promise( resolve => {
    setTimeout( () => {
        throw new Error('error');
    }, 2000);
})
    .then( value => {
    	console.log( value + ' world');
	}, value => {
    	console.log( 'Error: ', error.message);
	});

调用then()时的完成处理函数或拒绝处理函数如果抛出异常,都会导致(链中的)下一个promise因这个异常而立即被拒绝。

如果未定义错误处理函数,默认错误处理函数只是把错误重新抛出,使得错误可以继续沿着Promise链传播下去,直到遇到显式定义的错误处理函数。

如果将then()的第一个参数(完成处理函数)省略或传入任何非函数值,默认的处理完成函数会把接受到的任何传入值传递给下一个promise。

上面代码可以改写为

new Promise( resolve => {
    setTimeout( () => {
        throw new Error('error');
    }, 2000);
})
    .then( value => {
    	console.log( value + ' world'); // 不会执行
	}) // 这里使用的是默认错误处理函数
    .catch( error => {
    	console.log( 'Error: ', error.message); // 抛出异常
	});

捕获回调之后的异常信息,.then()不会执行,.catch()抛出异常。

.catch()与.then()的区别

.catch(...)只接受一个拒绝回调作为参数,并自动替换默认完成回调。等价于then(null, ...)

somePromise().then(function () {
  throw new Error('oh noes');
}).catch(function (err) {
  // I caught your error! :)
});

somePromise().then(function () {
  throw new Error('oh noes');
}, function (err) {
  // I didn't catch your error! :(
});

当使用.then(resolveHandler, rejectHandler)这种形式时,rejectHandler并不会捕获由resolveHandler引发的异常。

.catch()后继续使用.then()及.catch()的情况:

.catch()返回的是一个promise实例,如果.catch()中没有异常抛出,会正常执行后续的.then(),如果抛出异常会被后面的.catch()接收到进行异常处理。

注意:在所有的队列最后都加上.catch(),以避免漏掉错误处理造成意想不到的问题。

catch(…)会返回一个promise。任何Promise链的最后异步,不管是什么,总是存在着在未被查看的Promise中出现未捕获错误的可能性。

浏览器的垃圾回收机制可以进行处理最后的promise。

链式流

Promise固有行为特性:

  • 每次对Promise调用then(…),它都会创建并返回一个新的Promise
  • 不管从then(…)调用的完成回调返回值是什么,它都会被自动设置为被链接Promise(第一点中的)的完成

Promise.resolve(...)会直接返回接收到的真正Promise,或展开接收到的thenable值,并在持续展开thenable的同时递归地前进。

从完成(或拒绝)处理函数返回thenable或者Promise的时候也会发生同样的展开。

var p = Promise.resolve(21)

p.then(function(v) {
    console.log(v)
    
    // 创建一个promise并将其返回
    return new Promise(function(resolve, reject) {
        resolve(v * 2)
    })
}).then(function(v) {
    console.log(v)
})

// 21
// 42

任何Promise链的最后一步,不管是什么,总是存在着在未被查看的Promise中出现未捕获错误的可能性。浏览器可以追踪Promise对象。如果在它被垃圾回收的时候其中有拒绝,浏览器就能够确保这是一个真正的未捕获错误,进而可以确定应该将其报告到开发终端。

Promise 常用函数

1.Promise.resolve() 返回一个以个定制解析后的Promise对象

如果这个值是thenable(带有.then()方法),返回的Promise会“跟随”这个thenable对象,采用他的最终状态(指resolved/rejected/pending),会立刻执行.then()函数;如果传入的value本身就是promise对象,则该对象作为Promise.resolve方法的返回值返回;否则以改为成功状态。
返回promise对象,如果返回值是promise对象,则不做任何改变。

如果向Promise.resolve(...)传递一个非Promise、非thenable的立即值(如常量),就会得到一个用这个值填充的promise。

// p1和p2完全一样
var p1 = new Promise(function(resolve, reject) {
    resolve(42)
})
var p2 = Promise.resolve(42)
p1 === p2 // true

如果向Promise.resolve(...)传递一个真正的Promise,就只会返回同一个promise。

var p1 = Promise.resolve(42)
var p2 = Promise.resolve(p1)
p1 === p2 // true

Promise.resolve(...)可以接受收任何thenable,then(...)操作会按照promise进行响应。

// 非promise的thenable
var p = {
	then: function(cb,errcb) {
		cb( 42 );
		errcb( "evil laugh" );
	}
};

p
.then(
	function fulfilled(val){
		console.log( val ); // 42
	},
	function rejected(err){
		// 这里本不该运行
		console.log( err ); // evil laugh
	}
);

// 使用promise
Promise.resolve( p )
.then(
	function fulfilled(val){
		console.log( val ); // 42
	},
	function rejected(err){
		// 永远不会跑到这里
	}
);

如果不确定调用的函数是thenable,可以使用Promise.resolve进行包裹

2.Promise.reject() 返回失败状态的Promise对象

返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法

3.Promise.all([…]) 批量执行,所有完成才完成,有一个失败就立即结束

Promise.all([p1, p2, p3, …])用于将多个Promise实例包装成一个新的Pomise实例,即返回普通的Promise实例。

接受一个数组作为参数。

数组里可以是Promise对象,也可以是别的值,只有Promise会等待状态改变。

当所有的子Promise都完成,该Promise完成,返回值是全部Promise对象的返回值的数组。

有任何一个失败,该Promise立即失败,返回值是失败的子Promise的结果。

Promise.all获得的成功的结果中数组元素的顺序和Promise.all接收到的数组顺序是一致的(与完成顺序无关)。

如果传入空数组会立即完成。

常见用法:

.map()一起使用

function findLargest(dir) {
    return FileSystem.readDir(dir, 'utf-8')
        .then( files => { // 读取所有的文件
        return Promise.all( files.map( file => { // 把数组files中所有的元素都构建为一个Promise实例
            fs.stat(path.join(dir, file), (err, stat) => { // 读取文件
                if (err) throw err;
                if (stat.isDirectory()) {
                    return resolve({
                        size: 0
                    });
                }
                stat.file = file;
                resolve(stat); // 文件状态
            });
        }));// 读取所有文件之后返回一个Promise对象,并包含所有文件读取结果
    });
}
其他方式实现promise.all:
function promiseAll(values) {
    // 判断输入参数的合法性
    if (!Array.isArray(values)) {
        const type = typeof values;
        return new TypeError(`TypeError: ${type} ${values} is not iterable`)
    }
    // 返回一个包含数组结果的promise
    return new Promise((resolve, reject) => {
        const resultArr = []; // 结果数组
        let orderIndex = 0; // 记录返回结果是否全部结束
        // 遍历数组
        for (let i = 0; i < values.length; i++) {
            let value = values[i];
   			// value如果是thenable,Promise.resolve包裹后.then的值是promise执行后的值或响应thenable的then函数,如果不是thenable则是原值
            // 使用Promise.resolve确保是一个promise
            Promise.resolve(value).then(value => {
                // 将运行结果放到数组里的回调 
                resultArr[i] = value;
                if (++orderIndex === values.length) { // 判断是否所有的promise都返回了结果
                    resolve(resultArr)
                }
            }, reject) // 如果失败就直接返回失败,不处理返回成功的结果
        }
    });
}

// ==========================================================================
// 测试
function sleep(delay = 0) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(`sleep: ${delay}`);
        }, delay);
    })
}

function sleepReject(delay = 0) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(`sleepReject: ${delay}`);
        }, delay);
    })
}

promiseAll([1, sleepReject(100), 2, 3, sleep(50)]).then(data => {
    console.log('resolve', data);
}, err => {
    console.log('reject', err);
}) // reject sleepReject: 100

promiseAll([1, sleep(100), 2, 3, sleep(50)]).then(data => {
    console.log('resolve', data);
}, err => {
    console.log('reject', err);
}) // resolve [ 1, 'sleep: 100', 2, 3, 'sleep: 50' ]
4.Promise.race([…]),类似Promise.all有任意一个完成就算完成。

不需要等待所有的promise完成,任意一个promise完成或失败,就是最后的结果。

如果传入空数组,会挂住,永远不会改变为完成状态。

常见用法:

把异步操作和定时器放在一起,如果定时器先触发,就认为超时,告知用户。

// 一个使Promise超时的工具
function timeoutPromise(delay) {
	return new Promise( function(resolve,reject){
		setTimeout( function(){
			reject( "Timeout!" );
		}, delay );
	} );
}

// 为`foo()`设置一个超时
Promise.race( [
	foo(),					// 尝试调用`foo()`
	timeoutPromise( 3000 )	// 给它3秒钟
] )
.then(
	function(){
		// `foo(..)`及时地完成了!
	},
	function(err){
		// `foo()`不是被拒绝了,就是它没有及时完成
		// 那么可以考察`err`来知道是哪种情况
	}
);
5.Promise.none([…]) 所有promise都被拒绝

类似于Promise.all([…])。所有的promise都要被拒绝,即拒绝转化未完成值。

6.Promise.any([…]) 只需要完成一个

接收一个Promise对象的集合,当其中的一个 promise 成功,就返回那个成功的promise的值。

7.Promise.allSettled([…]) 返回所有的promise结果

Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilledrejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。

使用Promise.allSettled()方法可以批量处理promise的拒绝情况。

注意兼容,使用promise.all改写:

function sleep(delay = 0) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(`sleep: ${delay}`);
        }, delay);
    }).catch(err => console.log(err))
}

function sleepReject(delay = 0) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(`sleepReject: ${delay}`);
        }, delay);
    }).catch(err => console.log(err)) // 添加错误处理,catch返回默认的promise(默认的promise返回结果为undefined)
}

// promise.all中,出错的promise如果有错误处理函数则是错误处理函数处理过的promise
Promise.all([1, sleepReject(100), 2, 3, sleep(50)]).then(data => {
    console.log('resolve', data);
}, err => {
    console.log('reject', err);
})
// resolve [ 1, undefined, 2, 3, 'sleep: 50' ]

Promise常见用法

1.把回调包装成Promise,好处:可读性更好,返回的结果可以加入任何Promise队列。

2.把任何异步操作包装成Promise。

注意:JQuery3.0+版本已经实现完整的Promise对象

兼容IE,只想实现异步队列,Jquery.defered;兼容所有的平台,Bluebird类库或Promise polyfill兼容库。

3.多个api请求,链式调用,在第一个api调用结束之后.then()返回值中return新的promise对象调用第二个接口,在接下来的.then()函数中处理第二个api调用结果。

thenable

识别Promise(或者行为类似于Promise的东西)就是定义某种成为thenable的东西,将其定义为任何具有then(...)方法的对象和函数。任何这样的值就是Promise一致的thenable。

// 鸭子类型判断thenable
if (
	p !== null &&
    (
    	typeof p === 'object' ||
        typeof p === 'function'
    ) && 
    typeof p.then === 'function'
) {
    // 假定这是thenable
} else {
    // 不是thenable
}

Promise局限性

  1. 顺序错误处理

    Promise链中的错误很容易被无意中默默忽略掉。如果构建了一个没有错误处理函数的Promise链,链中任何地方的任何错误都会在链中一直传播下去,直到被查看。

  2. 单一值

    Promise只能有一个完成值或一个拒绝理由。需要将多个传参用对象或数组包裹住。

  3. 单决议

  4. 惯性

    用promise封装和兼容已有的代码。

  5. 无法取消的Promise

    一旦创建了一个Promise并为其注册了完成和/或拒绝处理函数,如果出现某种情况使得这个任务悬而未决的话,你也没有办法从外部停止它的进程。(async/await出现的契机)

  6. Promise性能

    Promise使所有一切都成为异步的了,即有一些立即(同步)完成的步骤仍然会延迟到任务的下一步。意味着一个Promise任务序列可能比完全通过回调链接的同样的任务序列运行的稍慢一些。

    Promise会有一些性能损耗,但是可以规避回调地狱。

实现一个满足PromiseA+规范的Promise

参考链接:Promise A+ https://promisesaplus.com/

手写Promise实现符合promise A+规范的方法

  1. then 的参数 onFulfilledonRejected 可以缺省,如果 onFulfilled 或者 onRejected不是函数,将其忽略,且依旧可以在下面的 then 中获取到之前返回的值;「规范 Promise/A+ 2.2.1、2.2.1.1、2.2.1.2」
  2. promise 可以 then 多次,每次执行完 promise.then 方法后返回的都是一个“新的promise”;「规范 Promise/A+ 2.2.7」
  3. 如果 then 的返回值 x 是一个普通值,那么就会把这个结果作为参数,传递给下一个 then 的成功的回调中;
  4. 如果 then 中抛出了异常,那么就会把这个异常作为参数,传递给下一个 then 的失败的回调中;「规范 Promise/A+ 2.2.7.2」
  5. 如果 then 的返回值 x 是一个 promise,那么会等这个 promise 执行完,promise 如果成功,就走下一个 then 的成功;如果失败,就走下一个 then 的失败;如果抛出异常,就走下一个 then 的失败;「规范 Promise/A+ 2.2.7.3、2.2.7.4」
  6. 如果 then 的返回值 x 和 promise 是同一个引用对象,造成循环引用,则抛出异常,把异常传递给下一个 then 的失败的回调中;「规范 Promise/A+ 2.3.1」
  7. 如果 then 的返回值 x 是一个 promise,且 x 同时调用 resolve 函数和 reject 函数,则第一次调用优先,其他所有调用被忽略;「规范 Promise/A+ 2.3.3.3.3」
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

const resolvePromise = (promise2, x, resolve, reject) => {
    // 自己等待自己完成是错误的实现,用一个类型错误,结束掉 promise  Promise/A+ 2.3.1
    if (promise2 === x) { 
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    // Promise/A+ 2.3.3.3.3 只能调用一次
    let called;
    // 后续的条件要严格判断 保证代码能和别的库一起使用
    if ((typeof x === 'object' && x != null) || typeof x === 'function') { 
        try {
            // 为了判断 resolve 过的就不用再 reject 了(比如 reject 和 resolve 同时调用的时候)  Promise/A+ 2.3.3.1
            let then = x.then;
            if (typeof then === 'function') { 
                // 不要写成 x.then,直接 then.call 就可以了 因为 x.then 会再次取值,Object.defineProperty  Promise/A+ 2.3.3.3
                then.call(x, y => { // 根据 promise 的状态决定是成功还是失败
                    if (called) return;
                    called = true;
                    // 递归解析的过程(因为可能 promise 中还有 promise) Promise/A+ 2.3.3.3.1
                    resolvePromise(promise2, y, resolve, reject); 
                }, r => {
                    // 只要失败就失败 Promise/A+ 2.3.3.3.2
                    if (called) return;
                    called = true;
                    reject(r);
                });
            } else {
                // 如果 x.then 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.3.4
                resolve(x);
            }
        } catch (e) {
            // Promise/A+ 2.3.3.2
            if (called) return;
            called = true;
            reject(e)
        }
    } else {
        // 如果 x 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.4  
        resolve(x)
    }
}

class Promise {
    constructor(executor) {
        this.status = PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks= [];

        let resolve = (value) => {
            if(this.status ===  PENDING) {
                this.status = FULFILLED;
                this.value = value;
                this.onResolvedCallbacks.forEach(fn=>fn());
            }
        } 

        let reject = (reason) => {
            if(this.status ===  PENDING) {
                this.status = REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn=>fn());
            }
        }

        try {
            executor(resolve,reject)
        } catch (error) {
            reject(error)
        }
    }

    then(onFulfilled, onRejected) {
        //解决 onFufilled,onRejected 没有传值的问题
        //Promise/A+ 2.2.1 / Promise/A+ 2.2.5 / Promise/A+ 2.2.7.3 / Promise/A+ 2.2.7.4
        // 箭头函数返回一个匿名函数,默认onFulfilled函数为接收传参并返回传参,默认onRejected函数为抛出错误
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
        //因为错误的值要让后面访问到,所以这里也要抛出个错误,不然会在之后 then 的 resolve 中捕获
        onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
        // 每次调用 then 都返回一个新的 promise  Promise/A+ 2.2.7
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === FULFILLED) {
                //Promise/A+ 2.2.2
                //Promise/A+ 2.2.4 --- setTimeout
                setTimeout(() => {
                    try {
                        //Promise/A+ 2.2.7.1
                        let x = onFulfilled(this.value);
                        // x可能是一个proimise
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        //Promise/A+ 2.2.7.2
                        reject(e)
                    }
                }, 0);
            }

            if (this.status === REJECTED) {
                //Promise/A+ 2.2.3
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                }, 0);
            }

            if (this.status === PENDING) {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e)
                        }
                    }, 0);
                });

                this.onRejectedCallbacks.push(()=> {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0);
                });
            }
        });

        return promise2;
    }
}

// ======================================================================
// 测试
const promise = new Promise((resolve, reject) => {
    reject('失败');
}).then().then().then(data=>{
    console.log(data);
},err=>{
    console.log('err',err);
})

练习

1:下面的四种promise的区别时什么 (来自we have a problem with promise)
// #1
doSomething().then(function () {
    return doSomethingElse();
}).then(finalHandler);

// #2
doSomething().then(function () {
    doSomethingElse();
}).then(finalHandler);

// #3
doSomething().then(doSomethingElse()).then(finalHandler);

// #4
doSomething().then(doSomethingElse).then(finalHandler);

(1)doSomething()执行完成之后会执行doSomethingElse(),doSomethingElse()接收不到doSomething的返回结果,然后再继续往下执行.then(finalHandler),finalHandler可以获取到doSomethingElse()中的返回结果。

doSomething
|-----------------|
                  doSomethingElse(undefined)
                  |------------------|
                                     finalHandler(resultOfDoSomethingElse)
                                     |------------------|

(2)doSomething()执行完成之后会同时执行doSomethingElse()和下一步.then(finalHandler),下一步中的finalHandler获取不到doSomethingElse()中的结果。

doSomething
|-----------------|
                  doSomethingElse(undefined)
                  |------------------|
                  finalHandler(undefined)
                  |------------------|

(3)doSomething()和doSomethingElse()是同时执行的,.then()中传递的doSomethingElse是promise实例,然而后面第二个.then()只会处理doSomething()的返回结果,会忽略掉doSomethingElse()的执行结果。

doSomething
|-----------------|
doSomethingElse(undefined)
|---------------------------------|
                  finalHandler(resultOfDoSomething)
                  |------------------|

(4)doSomething()会先执行,然后doSomethingElse()处理doSomething的返回结果,doSomethingElse()执行结束后下一个.then()再处理doSomethingElse()的返回结果。

doSomething
|-----------------|
                  doSomethingElse(resultOfDoSomething)
                  |------------------|
                                     finalHandler(resultOfDoSomethingElse)
                                     |------------------|

*注意:永远都是往then()中传递函数

2:promise调度

一个Promise决议后,这个Promise上所有的通过then(…)注册的回调都会在下一个异步时机点上依次被立即调用。这些回调中的任意一个都无法影响或延误对其他回调的调用。

p.then(function() {
    p.then(function() {
        console.log('C') // C无法打断或抢占B
    })
    console.log('A')
})
p.then(function() {
    console.log('B')
})
// A B C

// 两个独立Promise上链接的回调的相对顺序无法可靠预测
var p3 = new Promise(function(resolve, reject) {
    console.log('p3')
    resolve('B')
}) // p3为已决议状态

var p1 = new Promise(function(resolve, reject) {
    console.log('p1')
    resolve(p3) // 调用p3已决议结果
}) // p1为已决议状态

var p2 = new Promise(function(resolve, reject) {
    console.log('p2')
    resolve('A')
}) // p2为已决议状态

p1.then(function(v) {
    console.log(v)
})

p2.then(function(v) {
    console.log(v)
})
// p3
// p1
// p2
// A
// B
3:Promise辅助工具
// polyfill安全的guard检查
if (!Promise.wrap) {
    Promise.wrap = function(fn) {
        return function() {
            var args = [].slice.call(arguments)
            // 返回的函数自动创建一个Promise并返回,并替换回调,连接到Promise完成或拒绝
            return new Promise(function(resolve, reject) {
                fn.apply(
                	null,
                    args.concat(function(err, v) { // error-first回调
                        if (err) {
                            reject(err)
                        } else {
                            resolve(v)
                        }
                    })
                )
            })
        }
    }
}

// ==================================================================
// 使用
var request = Promise.wrap(ajax) // ajax响应后就会产生一个Promise
request('http://some.url.1/')
.then(...)

Promise.wrap(…)产出的是一个将产生Promise的函数,像Promise工厂。

把需要回调的函数封装为支持Promise的函数,可以称为Promise工厂化或提升。

循环输出内容

输出以下代码运行结果,为什么?如果希望每隔 1s 输出一个结果,应该如何改造?注意不可改动 square 方法

问题链接:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/389

const list = [1, 2, 3]
const square = num => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(num * num)
    }, 1000)
  })
}

forEach是不能阻塞的,默认是请求并行发起

方法:

// 方法一
async function test() {
    for (let i = 0; i < list.length; i++) {
        let x = list[i]
        const res = await square(x)
        console.log(res)
    }
}

// 方法二
async function test() {
    for (let x of list) {
      const res = await square(x)
      console.log(res)
    }
}

// 方法三
function test(i = 0, promise = Promise.resolve()) {
  if (i > list.length) return
  promise = promise.then(res => {
    console.log(res)
    return square(list[i])
  })
  test(i + 1, promise)
}

test()

参考链接:

[翻译]We have a problem with promises:http://fex.baidu.com/blog/2015/07/we-have-a-problem-with-promises/

慕课网免费课程《Promise入门》

《你不知道的JavaScript(中卷)》第三章Promise: https://github.com/getify/You-Dont-Know-JS/blob/1ed-zh-CN/async%20%26%20performance/ch3.md

Promise A+: https://promisesaplus.com/

知乎-你不知道的permise:https://zhuanlan.zhihu.com/p/288384170