Last active
April 10, 2021 21:49
-
-
Save Jamlee/c8cb6fc60df9c0e48c49648eef593fe7 to your computer and use it in GitHub Desktop.
异步: promise, javascript 实现
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var fs = require('fs'); | |
var readFile = function (fileName){ | |
return new Promise(function (resolve, reject){ | |
fs.readFile(fileName, function(error, data){ | |
if (error) reject(error); | |
resolve(data); | |
}); | |
}); | |
}; | |
var gen = function* (){ | |
var f1 = yield readFile('/etc/fstab'); | |
var f2 = yield readFile('/etc/shells'); | |
console.log(f1.toString()); | |
console.log(f2.toString()); | |
} | |
var g = gen(); | |
function run(gen){ | |
var g = gen(); | |
function next(data){ | |
var result = g.next(data); | |
if (result.done) return result.value; | |
result.value.then(function(data){ | |
next(data); | |
}); | |
} | |
next(); | |
} | |
run(gen); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//一个简易的promise实现 | |
fs = require('fs') | |
const util = require('util') | |
var PENDING = 0; | |
var FULFILLED = 1; | |
var REJECTED = 2; | |
function Promise(fn) { | |
// store state which can be PENDING, FULFILLED or REJECTED | |
var state = PENDING; | |
// store value once FULFILLED or REJECTED | |
var value = null; | |
// store sucess & failure handlers | |
var handlers = []; | |
this.done = function (onFulfilledHandler, onRejectedHandler) { | |
// 确保是异步操作 | |
setTimeout(function () { | |
handle({ | |
onFulfilled: onFulfilledHandler, | |
onRejected: onRejectedHandler | |
}); | |
}, 0); | |
} | |
this.then = function (onFulfilled, onRejected) { | |
var self = this; | |
return new Promise(function (resolve, reject) { | |
self.done(function (result) { | |
if (typeof onFulfilled === 'function') { | |
try { | |
return resolve(onFulfilled(result)); | |
} catch (ex) { | |
return reject(ex); | |
} | |
} else { | |
return resolve(result); | |
} | |
}, function (error) { | |
if (typeof onRejected === 'function') { | |
try { | |
return resolve(onRejected(error)); | |
} catch (ex) { | |
return reject(ex); | |
} | |
} else { | |
return reject(error); | |
} | |
}); | |
}); | |
} | |
function reject(error) { | |
console.log('4. 异常-> 解析第一个异步操作返回的结果') | |
state = REJECTED; | |
value = error; | |
// 处理内部的handlers列表 | |
handlers.forEach(handle); | |
handlers = null; | |
} | |
function resolve(result) { | |
console.log('4. 正常-> 解析第一个异步操作返回的结果') | |
function fulfill(result) { | |
state = FULFILLED; | |
value = result; | |
// 处理内部的handlers列表 | |
handlers.forEach(handle); | |
handlers = null; | |
} | |
function getThen(value) { | |
var t = typeof value; | |
if (value && (t === 'object' || t === 'function')) { | |
var then = value.then; | |
if (typeof then === 'function') { | |
return then; | |
} | |
} | |
return null; | |
} | |
try { | |
var then = getThen(result); | |
if (then) { | |
console.log('4.1 如果返回的结果是thenable,继续调用doResolve') | |
doResolve(then.bind(result), resolve, reject) | |
return | |
} | |
console.log('4.1 如果返回的结果不是thenable, 将当前的promise对象的状态改为 `fulfill`') | |
fulfill(result); | |
} catch (e) { | |
reject(e); | |
} | |
} | |
function handle(handler) { | |
if (state === PENDING) { | |
handlers.push(handler); | |
} else { | |
if (state === FULFILLED && | |
typeof handler.onFulfilled === 'function') { | |
console.log('5. 操作完成, 执行用户定义的done函数-正常') | |
handler.onFulfilled(value); | |
} | |
if (state === REJECTED && | |
typeof handler.onRejected === 'function') { | |
onsole.log('5. 操作完成, 执行用户定义的done函数-异常') | |
handler.onRejected(value); | |
} | |
} | |
console.log(handlers) | |
} | |
/** | |
* | |
*/ | |
function doResolve(fn, onFulfilled, onRejected) { | |
var done = false; | |
try { | |
//执行异步函数 | |
fn(/* fulfill函数 */function (value) { | |
//本函数对promise 工厂函数内部的的resolve函数进行 `装饰` | |
if (done) return | |
console.log('3. 正常情况下, 改变promise对象的状态') | |
done = true | |
onFulfilled(value) | |
}, /* rejected函数 */ function (reason) { | |
//对promise 工厂函数内部的的reject函数 进行 `装饰` | |
if (done) return | |
console.log('3. 异常情况下,改变promise对象的状态') | |
done = true | |
onRejected(reason) | |
}) | |
} catch (ex) { | |
if (done) return | |
done = true | |
onRejected(ex) | |
} | |
} | |
console.log('2. 生成一个Pormise对象') | |
console.log(' ' + util.inspect(this, {showHidden: false, depth: null})) | |
/** | |
* 将函数与promise工厂的resolve, reject 连接起来 | |
*/ | |
doResolve(fn, resolve, reject) | |
} | |
/** | |
* Demo | |
* | |
* 本地异步读取文件信息 | |
*/ | |
console.log('1. 包装异步操作为Promise') | |
function readFile(filename, enc) { | |
/** | |
* fulfill, reject 由Promise工厂提供 | |
*/ | |
return new Promise(function (fulfill, reject) { | |
fs.readFile(filename, enc, function (err, res) { | |
if (err) reject(err); | |
else fulfill(res); | |
}); | |
}); | |
} | |
filename = './test.json' | |
promise = readFile(filename, 'utf8'); | |
// promise.done(function (res) { | |
// console.log('读取成功!!!') | |
// return '步骤二' | |
// }, function () { | |
// console.log('读取出错!!!') | |
// }); | |
promise.then(function (res) { | |
console.log('链式调用第一环') | |
}, function (err) { | |
console.log('链式调用第一环错误') | |
}).then(function (res) { | |
console.log('链式调用第二环') | |
}, function (err) { | |
console.log('链式调用第二环错误') | |
}); | |
/** | |
* 结论: | |
* | |
* 1. Promise 是一个 `工厂方法`, 所以Promise对象通过闭包保留了对函数内部定义的函数对象、普通对象的引用。换句话说,每个Promise对象的有自己的state、handler、value。 | |
* | |
* 2. 当new Promise(function (resolve, reject){}) 时, 实际上参数中的异步操作已经执行并且获得结果, done方法仅仅处理调用后的结果。那么这里存在一个问题如何保证在调用done时,异步操作已经操作完成? 调用done时,如果当前的状态是 pending 就加入到handler中。如果 promise 初始化是在调用done之前完成,promise的state已经通过new Promise(function (resolve, reject){})中的fulfill函数变为fulfill,调用done时直接获得结果。 如果 promise 初始化是在调用done之后完成,done会处于在handlers中,等待new Promise(function (resolve, reject){})中的fulfill被执行。 | |
* | |
* 3. promise对象的接口: resolve, reject, doResolve, handle, then, done | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment