whynotgonow 2020-05-12
1.promise常用方法
Promise.all() Promise.race() Promise.resolve() Promise.reject() Promise.prototype.catch() Promise.prototype.finally() Promise.prototype.then()2.考虑下面一种获取用户id的请求处理
//例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
怎么实现呢?其实按照上面一句话,实现一个最基础的雏形还是很easy的。
this.then = function (onFulfilled) { callbacks.push(onFulfilled); return this; };
getUserId().then(function (id) { // 一些处理 }).then(function (id) { // 一些处理 });
function Promise(fn) { var state = ‘pending‘, value = null, callbacks = []; this.then = function (onFulfilled) { if (state === ‘pending‘) { callbacks.push(onFulfilled); return this; } onFulfilled(value); return this; }; function resolve(newValue) { value = newValue; state = ‘fulfilled‘; setTimeout(function () { callbacks.forEach(function (callback) { callback(value); }); }, 0); } fn(resolve); }
function Promise(fn) { var state = ‘pending‘, value = null, callbacks = []; this.then = function (onFulfilled) { return new Promise(function (resolve) { handle({ onFulfilled: onFulfilled || null, resolve: resolve }); }); }; function handle(callback) { if (state === ‘pending‘) { callbacks.push(callback); return; } //如果then中没有传递任何东西 if(!callback.onFulfilled) { callback.resolve(value); return; } var ret = callback.onFulfilled(value); callback.resolve(ret); } function resolve(newValue) { if (newValue && (typeof newValue === ‘object‘ || typeof newValue === ‘function‘)) { var then = newValue.then; if (typeof then === ‘function‘) { then.call(newValue, resolve); return; } } state = ‘fulfilled‘; value = newValue; setTimeout(function () { callbacks.forEach(function (callback) { handle(callback); }); }, 0); } fn(resolve); }
getUserId() .then(getUserJobById) .then(function (job) { // 对job的处理 }); function getUserJobById(id) { return new Promise(function (resolve) { http.get(baseUrl + id, function(job) { resolve(job); }); }); }
then
方法中,创建并返回了新的Promise
实例,这是串行Promise
的基础,并且支持链式调用。handle
方法是Promise
内部的方法。then
方法传入的形参onFulfilled
以及创建新Promise
实例时传入的resolve
均被push
到当前Promise
的callbacks
队列中,这是衔接当前Promise
和后邻Promise
的关键所在(这里一定要好好的分析下handle的作用)。getUserId
生成的Promise
(简称getUserId promise
)异步操作成功,执行其内部方法resolve
,传入的参数正是异步操作的结果id
handle
方法处理callbacks
队列中的回调:getUserJobById
方法,生成新的Promise
(getUserJobById promise
)getUserId promise
的then
方法生成的新Promise
(称为bridge promise
)的resolve
方法,传入参数为getUserJobById promise
。这种情况下,会将该resolve
方法传入getUserJobById promise
的then
方法中,并直接返回。getUserJobById promise
异步操作成功时,执行其callbacks
中的回调:getUserId bridge promise
中的resolve
方法getUserId bridge promise
的后邻Promise
的callbacks
中的回调。//例5 function getUserId() { return new Promise(function(resolve) { //异步请求 http.get(url, function(error, results) { if (error) { reject(error); } resolve(results.id) }) }) } getUserId().then(function(id) { //一些处理 }, function(error) { console.log(error) })
有了之前处理fulfilled状态的经验,支持错误处理变得很容易,只需要在注册回调、处理状态变更上都要加入新的逻辑:
function Promise(fn) { var state = ‘pending‘, value = null, callbacks = []; this.then = function (onFulfilled, onRejected) { return new Promise(function (resolve, reject) { handle({ onFulfilled: onFulfilled || null, onRejected: onRejected || null, resolve: resolve, reject: reject }); }); }; function handle(callback) { if (state === ‘pending‘) { callbacks.push(callback); return; } var cb = state === ‘fulfilled‘ ? callback.onFulfilled : callback.onRejected, ret; if (cb === null) { cb = state === ‘fulfilled‘ ? callback.resolve : callback.reject; cb(value); return; } ret = cb(value); callback.resolve(ret); } function resolve(newValue) { if (newValue && (typeof newValue === ‘object‘ || typeof newValue === ‘function‘)) { var then = newValue.then; if (typeof then === ‘function‘) { then.call(newValue, resolve, reject); return; } } state = ‘fulfilled‘; value = newValue; execute(); } function reject(reason) { state = ‘rejected‘; value = reason; execute(); } function execute() { setTimeout(function () { callbacks.forEach(function (callback) { handle(callback); }); }, 0); } fn(resolve, reject);}
上述代码增加了新的reject
方法,供异步操作失败时调用,同时抽出了resolve
和reject
共用的部分,形成execute
方法。
错误冒泡是上述代码已经支持,且非常实用的一个特性。在handle
中发现没有指定异步操作失败的回调时,会直接将bridge promise
(then
函数返回的Promise
,后同)设为rejected
状态,如此达成执行后续失败回调的效果。这有利于简化串行Promise
的失败处理成本,因为一组异步操作往往会对应一个实际功能,失败处理方法通常是一致的:
//例6 getUserId() .then(getUserJobById) .then(function (job) { // 处理job }, function (error) { // getUserId或者getUerJobById时出现的错误 console.log(error); });
function handle(callback) { if (state === ‘pending‘) { callbacks.push(callback); return; } var cb = state === ‘fulfilled‘ ? callback.onFulfilled : callback.onRejected, ret; if (cb === null) { cb = state === ‘fulfilled‘ ? callback.resolve : callback.reject; cb(value); return; } try { ret = cb(value); callback.resolve(ret); } catch (e) { callback.reject(e); } }
如果在异步操作中,多次执行resolve
或者reject
会重复处理后续回调,可以通过内置一个标志位解决。