Promise 原理
//例1 function getUserId() { return new Promise(function(resolve) { //异步请求 http.get(url, function(results) { resolve(results.id) }) }) } getUserId().then(function(id) { //一些处理 })
getUserId方法返回一个promise,可以通过它的 then方法注册(注意注册这个词) 在promise异步操作成功时执行的回调。这种执行方式,使得异步调用变得十分顺手。
极简promise
function Promise(fn) { var value = null, callbacks = []; //callbacks为数组,因为可能同时有很多个回调 this.then = function (onFulfilled) { callbacks.push(onFulfilled); }; function resolve(value) { callbacks.forEach(function (callback) { callback(value); }); } fn(resolve); }
1、当调用then方法,将要在Promise异步操作成功时执行的回调放入callbacks队列
2、当用户调用 resolve(value) 方法时,会遍历执行 callbacks 队列中的任务
可以结合例子中的代码来看,首先new Promise时,传给promise的函数发送异步请求,接着调用promise对象的then属性,注册请求成功的回调函数,然后当异步请求发送成功时,调用resolve(results.id)方法, 该方法执行then方法注册的回调数组。
相信仔细的人应该可以看出来,then方法应该能够链式调用,但是上面的最基础简单的版本显然无法支持链式调用。想让then方法支持链式调用,其实也是很简单的:
this.then = function (onFulfilled) { callbacks.push(onFulfilled); return this; };
see?只要简单一句话就可以实现类似下面的链式调用:
// 例2 getUserId().then(function (id) { // 一些处理 }).then(function (id) { // 一些处理 });
加入延时机制
细心的同学应该发现,上述代码可能还存在一个问题:如果在then方法注册回调之前,resolve函数就执行了,怎么办?(就是使用 .then 的时候触发 this.then的方法注册回调函数)比如 promise 内部的函数是同步函数:
// 例3 function getUserId() { return new Promise(function (resolve) { resolve(9876); //先执行 }); } getUserId().then(function (id) { //.then后执行,注册回调前resolve已执行 // 一些处理 });
这显然是不允许的,Promises/A+规范明确要求回调需要通过异步方式执行,用以保证一致可靠的执行顺序。因此我们要加入一些处理,保证在resolve执行之前,then方法已经注册完所有的回调。我们可以这样改造下resolve函数:
function resolve(value) { setTimeout(function() { callbacks.forEach(function (callback) { callback(value); }); }, 0) }
上述代码的思路也很简单,就是通过setTimeout机制,将resolve中执行回调的逻辑放置到JS任务队列末尾,以保证在resolve执行时,then方法的回调函数已经注册完成.
浙公网安备 33010602011771号