1、简单介绍下Promise。
Promise
是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise
对象。有了Promise
对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数解决回调地狱。此外,Promise
对象提供统一的接口,使得控制异步操作更加容易。(当然了也可以简单介绍promise
状态,有什么方法,callback
存在什么问题等等)
2、实现一个简单的,支持异步链式调用的Promise类。
// [[Resolve]](promise2, x)函数
resolvePromise(promise2, x, resolve, reject) {
let called = false;
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise!'))
}
// 如果x仍然为Promise的情况
if (x instanceof Promise) {
// 如果x的状态还没有确定,那么它是有可能被一个thenable决定最终状态和值,所以需要继续调用resolvePromise
if (x.status === 'pending') {
x.then(function(value) {
resolvePromise(promise2, value, resolve, reject)
}, reject)
} else {
// 如果x状态已经确定了,直接取它的状态
x.then(resolve, reject)
}
return
}
if (x !== null && (Object.prototype.toString(x) === '[object Object]' || Object.prototype.toString(x) === '[object Function]')) {
try {
// 因为x.then有可能是一个getter,这种情况下多次读取就有可能产生副作用,所以通过变量called进行控制
const then = x.then
// then是函数,那就说明x是thenable,继续执行resolvePromise函数,直到x为普通值
if (typeof then === 'function') {
then.call(x, (y) => {
if (called) return;
called = true;
this.resolvePromise(promise2, y, resolve, reject);
}, (r) => {
if (called) return;
called = true;
reject(r);
})
} else { // 如果then不是函数,那就说明x不是thenable,直接resolve x
if (called) return ;
called = true;
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
3、Promise.then在Event Loop中的执行顺序。
JS
中分为两种任务类型:macrotask
和microtask
,其中macrotask
包含:主代码块,setTimeout
,setInterval
,setImmediate
等(setImmediate
规定:在下一次Event Loop
(宏任务)时触发);microtask
包含:Promise
,process.nextTick
等(在node
环境下,process.nextTick
的优先级高于Promise
)Event Loop
中执行一个macrotask
任务(栈中没有就从事件队列中获取)执行过程中如果遇到microtask
任务,就将它添加到微任务的任务队列中,macrotask
任务执行完毕后,立即执行当前微任务队列中的所有microtask
任务(依次执行),然后开始下一个macrotask
任务(从事件队列中获取)
4、阐述Promise的一些静态方法。
Promise.all
、Promise.race
、Promise.resolve
、Promise.reject
等
5、Promise存在哪些缺点
1、无法取消Promise
,一旦新建它就会立即执行,无法中途取消。
2、如果不设置回调函数,Promise
内部抛出的错误,不会反应到外部。
3、吞掉错误或异常,错误只能顺序处理,即便在Promise
链最后添加catch
方法,依然可能存在无法捕捉的错误(catch
内部可能会出现错误)
4、阅读代码不是一眼可以看懂,你只会看到一堆then
,必须自己在then
的回调函数里面理清逻辑。
6、使用Promise进行顺序(sequence)处理。
1、使用async
函数配合await
或者使用generator
函数配合yield
。
2、使用promise.then
通过for
循环或者Array.prototype.reduce
实现。
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());
}
7、如何停止一个Promise链?
在要停止的promise
链位置添加一个方法,返回一个永远不执行resolve
或者reject
的Promise
,那么这个promise
永远处于pending
状态,所以永远也不会向下执行then
或catch
了。这样我们就停止了一个promise
链。
Promise.cancel = Promise.stop = function() {
return new Promise(function(){})
}
8、Promise链上返回的最后一个Promise出错了怎么办?
catch
在promise
链式调用的末尾调用,用于捕获链条中的错误信息,但是catch
方法内部也可能出现错误,所以有些promise
实现中增加了一个方法done
,done
相当于提供了一个不会出错的catch
方法,并且不再返回一个promise
,一般用来结束一个promise
链。
done() {
this.catch(reason => {
console.log('done', reason);
throw reason;
});
}
9、Promise存在哪些使用技巧或者最佳实践?
1、链式promise
要返回一个promise
,而不只是构造一个promise
。
2、合理的使用Promise.all
和Promise.race
等方法。
3、在写promise
链式调用的时候,then
方法不传onRejected
函数,只需要在最末尾加一个catch()
就可以了,这样在该链条中的promise
发生的错误都会被最后的catch
捕获到。如果catch()
代码有出现错误的可能,需要在链式调用的末尾增加done()
函数。