要理解Promise的执行原理,首先要知道EMCAScript的任务队列机制——macro-task(宏任务)和micro-task(微任务)。
浏览器先执行一个macrotask,在执行macrotask过程中可能会创建新的macrotask和microtask,然后顺序执行micro-task队列中的全部任务,然后再取出一个macrotask执行……周而复始。整个script代码是一个macrotask,setTimeout会产生一个新的macrotask,Promise对象调用到resolve时会创建一个microtask。想要更细节的理解,传送门Promise的队列与setTimeout的队列有何关联?
下面介绍Promise中resolve的调用机制。
function wash(resolve){ console.log('开始洗衣服...'); setTimeout(()=>{ console.log('洗完了!'); resolve('一堆洗干净的衣服'); }, 2000); }
function hang(clothes){ console.log('开始晾衣服...'); /*...晾衣服中...*/ console.log(clothes+'晾好了!'); return '一堆晾好的衣服'; } function dry(clothes){ console.log('等衣服干...'); /*...晾干中...*/ console.log(clothes+'晾干了!'); return '一堆晾干的衣服'; } function pickup(clothes){ console.log('开始收衣服...'); /*...收衣服中...*/ console.log(clothes+'收完了!'); }
准备wash,hang,dry,pickup四个方法,每个方法有一行return语句,将处理后得到的结果输出,作为参数传入接下去的要处理的方法,按照如下方法执行:
var promise = new Promise(wash); promise.then(hang).then(dry).then(pickup);
输出:
开始洗衣服... 洗完了,去晾干! 开始晾衣服... 一堆洗干净的衣服晾好了! 等衣服干... 一堆晾好的衣服晾干了! 开始收衣服... 一堆晾干了的衣服收完了!
更复杂的例子,让晾衣服的各个步骤间隔一定时间:
function hang(clothes){ console.log('开始晾衣服...'); return new Promise(resolve=>{ setTimeout(()=>{ console.log(clothes+'晾完了!'); resolve("一堆晾好的衣服"); }, 3000) }); } function dry(clothes){ console.log('等衣服干...'); return new Promise(resolve=>{ setTimeout(()=>{ console.log(clothes+'晾干了!'); resolve('一堆晾干了的衣服'); }, 3000) }); } function pickup(clothes){ console.log('开始收衣服...'); setTimeout(()=>{ console.log(clothes+'收完了!'); }, 3000) }
只改变这几个步骤的方法,其他代码不变,这里hang和dry方法返回一个Promise对象,又由于执行时:
promise.then(hang).then(dry).then(pickup);then传入的方法如果返回的是个Promise对象,那么再后面的then传入的方法就会等到这个Promise(实际上是传入Promise的方法)调用了resolve()为止,才会继续执行。