Callback 函数
JS 早期实现异步都是采用回调方式 callback 来实现异步任务的包括事件回调, setTimeout,setInterval, Ajax 等, 采取这样的方式写代码, 功能实现没有问题, 但是不够优雅清晰, 容易出现回调地狱
Promise
Promise A plus 中文规范 英文规范
实现过程(一)
同步基础框架
- new Promise(function(resovle, reject){
- })
从上代码可以看出构造函数接受一个函数
- const PENDING = "pending"; // 等待
- const FULFILLED = "fulfilled"; // 已完成
- const REJECTED = "rejected"; // 已拒绝
- function Promise(executor) {
- let self = this;
- self.status = PENDING;
- self.value;
- self.reason;
- function resolve(value) {
- if (self.status === PENDING) {
- self.status = FULFILLED;
- self.value = value;
- }
- }
- function reject(reason) {
- if (self.status === PENDING) {
- self.status = REJECTED;
- self.reason = reason;
- }
- }
- try { // 规范提到, 执行器抛异常会 reject
- executor(resolve, reject);
- } catch(e) {
- reject(e)
- }
- }
then 方法实现
- Promise.prototype.then = function (onFulfilled, onRjected) {
- let self = this;
- /**
- * onFulfilled 和 onRejected 都是可选参数
- * 如果 onFulfilled 不是函数, 其必须被忽略
- * 如果 onRejected 不是函数, 其必须被忽略
- */
- onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(value) {
- return value;
- };
- onRjected = typeof onRjected === 'function' ? onRjected : function(reason) {
- throw reason;
- }
- if (self.status === FULFILLED) {
- onFulfilled(self.value);
- }
- if (self.status === REJECTED) {
- onRjected(self.reason);
- }
- }
实现过程(二)
异步调用实现
promise 实例可以多次 then, 当成功后会将 then 中的成功方法按顺序执行, 可以先将 then 中的成功的回调和失败的回调存到数组内, 当成功时调用成功或失败的数组
- // 添加 then 的回调队列
- self.onResolvedCallbacks = [];
- self.onRejectedCallbacks = [];
- // 成功时候调用队列里的 handler
- self.onResolvedCallbacks.forEach(function (fn, index, array) {
- fn()
- });
- // 失败时候调用队列里的 handler
- self.onRejectedCallbacks.forEach(function (fn, index, array) {
- fn()
- })
实现过程(三)
当链式调用 then()时候, jquery 能实现链式调用靠的就是返回 this, 但是 promise 不能返回 this,promise 实现链式调用靠的是返回一个新的 promise 而且 then()方法可以什么都不穿, 实现穿透调用下一个 then()...
- Promise.prototype.then = function (onFulfilled, onRjected) {
- let self = this;
- let promise2 = null; // 返回的 promise
- // 我们的代码可以在 then 中什么都不传 promise 中值的穿透
- onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
- return value;
- };
- onRjected = typeof onRjected === 'function' ? onRjected : function (reason) {
- throw reason;
- }
- if (self.status === FULFILLED) {
- promise2 = new Promise(function (onFulfilled2, onRjected2) {
- let result = onFulfilled(self.value);
- onFulfilled2(result);
- });
- }
- if (self.status === REJECTED) {
- promise2 = new Promise(function (onFulfilled2, onRjected2) {
- let reason = onRjected(self.reason);
- onRjected2(reason);
- });
- }
- if (self.status === PENDING) {
- promise2 = new Promise(function (resolve, reject) {
- self.onResolvedCallbacks.push(function () {
- let value = onFulfilled(self.value);
- resolve(value);
- });
- self.onRejectedCallbacks.push(function () {
- let reason = onRjected(self.reason);
- reject(reason);
- })
- });
- }
- return promise2;
- }
实现过程(四)
如果 then 中无论是成功的回调还是失败的回调, 只要返回了结果 (return xxx) 就会走下一个 then 中的成功, 如果有错误 (throw new Error) 就走下一个 then 的失败
如果第一个 promise 返回一个普通值, 会进到下一次 then 的成功的回调, 如果第一个 promise 返回了一个 promise, 需要等待返回的 promise 执行后的结果传递给下一次 then 中
- function resolvePromise(promise2, x, resolve, reject) {
- // 有可能这里返回的 x 是别人的 promise
- // 尽可能允许其他乱写
- // 返回的结果和 promise 是同一个那么永远不会成功和失败
- if (promise2 === x) {
- throw new Error('不能反悔自己')
- }
- let called;
- // 判断 Promise 类型
- if (typeof x !== 'object' && (typeof x === 'object' || typeof x === 'function')) {
- try {
- let then = x.then;
- // promise 可以是一个函数函数上有一个 then 方法
- if (typeof then === 'function') {
- then.call(x,
- function(y) {
- if (called) return;
- called = true;
- // y 可能还是一个 promise, 在去解析直到返回的是一个普通值
- resolvePromise(promise2, y, resolve, reject);
- },
- function(err) {
- if (called) return;
- called = true;
- reject(err);
- });
- } else {
- // 不是函数, 又是一个 promise
- resolve(x)
- }
- } catch(err) {
- if (called) return called = true;
- reject(err);
- }
- } else {
- // 一般类型返回
- //3. 如果 then 中无论是成功的回调还是失败的回调只要返回了结果就会走下一个 then 中的成功,
- resolve(x);
- }
- }
- Promise.prototype.then = function (onFulfilled, onRjected) {
- let self = this;
- let promise2 = null;
- onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
- return value;
- };
- onRjected = typeof onRjected === 'function' ? onRjected : function (reason) {
- throw reason;
- }
- if (self.status === FULFILLED) {
- promise2 = new Promise(function (onFulfilled2, onRjected2) {
- setTimeout(function () { //9.promise 规范中要求, 所有的 onFufiled 和 onRjected 都需要异步执行, setTimeout
- try {
- let result = onFulfilled(self.value);
- resolvePromise(promise2, result, onFulfilled, onRjected);
- } catch (err) {
- onRjected2(err);// 如果有错误走下一个 then 的失败
- }
- });
- });
- }
- if (self.status === REJECTED) {
- promise2 = new Promise(function (onFulfilled2, onRjected2) {
- setTimeout(function () { //9.promise 规范中要求, 所有的 onFufiled 和 onRjected 都需要异步执行, setTimeout
- try {
- let reason = onRjected(self.reason);
- resolvePromise(promise2, reason, onFulfilled, onRjected);
- } catch (err) {
- onRjected2(err);// 如果有错误走下一个 then 的失败
- }
- });
- });
- }
- if (self.status === PENDING) {
- promise2 = new Promise(function (resolve, reject) {
- self.onResolvedCallbacks.push(function () {
- setTimeout(function () { //9.promise 规范中要求, 所有的 onFufiled 和 onRjected 都需要异步执行, setTimeout
- try {
- let value = onFulfilled(self.value);
- resolvePromise(promise2, value, resolve, reject);
- } catch (err) {
- reject(err); // 如果有错误走下一个 then 的失败
- }
- });
- });
- self.onRejectedCallbacks.push(function () {
- setTimeout(function () { //9.promise 规范中要求, 所有的 onFufiled 和 onRjected 都需要异步执行, setTimeout
- try {
- let reason = onRjected(self.reason);
- resolvePromise(promise2, reason, resolve, reject);
- } catch (err) {
- reject(err);// 如果有错误走下一个 then 的失败
- }
- });
- })
- });
- }
- return promise2;
- }
实现过程(五)
实现 catch, all, race, resolve, reject, defer 这些事例方法
- // 捕获错误的方法
- // 相当于不要 then 的第一个成功回调
- Promise.prototype.
- catch = function(callback) {
- return this.then(null, callback)
- };
- Promise.prototype.all = function(promises) {
- return new Promise(function(resolve, reject) {
- let returns = [];
- let index = 0;
- function processData(i, data) {
- returns[i] = data;
- if (++index === promises.length) {
- resolve(returns)
- }
- }
- for (let i = 0; i <= promises.length; i++) {
- promises[i].then(function(data) {
- processData(i, data)
- },
- reject);
- }
- })
- };
- Promise.prototype.race = function(promises) {
- return new Promise(function(resolve, reject) {
- promises.forEach(function(value) {
- value.then(resolve, reject);
- })
- })
- };
- Promise.resolve = function(value) {
- return new Promise(function(resolve, reject) {
- resolve(value)
- });
- };
- Promise.reject = function(reason) {
- return new Promise(function(resolve, reject) {
- reject(reason)
- })
- };
- Promise.defer = Promise.deferred = function() {
- let dfd = {};
- return new Promise(function(resolve, reject) {
- dfd.resolve = resolve;
- dfd.reject = reject;
- });
- return dfd;
- };
- Genernator
Genernator 函数要用 * 来比标识, yield(暂停 产出), 它会将函数分割出好多个部分, 调用一次 next 就会继续向下执行, 返回结果是一个迭代器, 迭代器有一个 next 方法, yield 后面跟着的是 value 的值, yield 等号前面的是我们当前调用 next 传进来的值, 第一次 next 传值是无效的
- function* read() {
- console.log('init');
- let a = yield '第一波汤圆';
- console.log(a);
- let b = yield '第二波汤圆'
- console.log(b);
- return b;
- }
- let it = read();
- console.log(it.next('给我汤圆'));
- // init
- // {value:'第一波汤圆',done:false}
- console.log(it.next('我还要汤圆'));
- // 我还要汤圆
- // {value:'第二波汤圆',done:false}
- console.log(it.next('够了不要了'));
- // 够了不要了
- // {value: 第二波汤圆, done:true}
- console.log(it.next('还有么?'));
- // {value:undefined,done:true}
可以看到, 当调用 read()时候, 程序仅仅返回一个迭代器函数内代码并没有运行
提一次调用 next('给我汤圆'), 后函数运行了, 如下内容:
- console.log('init');
- yield '第一波汤圆';
- // 所以答应出来
- // init
- // 并且返回{value:'第一波汤圆',done:false}
- // 注意, 第一次传入的'给我汤圆' 函数并没有接收处理
第二次调用 console.log(it.next('我还要汤圆')), 程序运行了如下内容:
- let a = '我还要汤圆';
- console.log(a);
- yield '第二波汤圆'
- // 我还要汤圆
- // {value:'第二波汤圆',done:false}
- // 将传入参宿赋值给 a, 并打印出来, 然后返回 yield 后的'第二波汤圆'
第三次调用 console.log(it.next('够了不要了')), 程序运行了如下内容:
- let b = '够了不要了'
- console.log(b);
- return b;
- // 够了不要了
- // {value: 够了不要了, done:true}
- // 将传入参宿赋值给 b, 并打印出来, 然后返回 b 的'够了不要了', 这是迭代结束, 返回的 done 就变为 true
继续调用 console.log(it.next('还有么?')), 程序没有迭代内容了, 所以直接返回
// {value:undefined, done:true}
Generator 主要和 Promise 搭配使用
- let bluebird = require('bluebird'); // bluebird 见相关函数库介绍
- let fs = require('fs');
- let read = bluebird.promisify(fs.readFile);
- function * gRead() {
- let content1 = yield read('./1.txt', 'utf8');
- // content1 存放的是 content2 的文件地址
- let content2 = yield read(content1, 'utf8');
- return content2;
- }
- // 接下来我要一次一次的调用 next 才能得到我要的 content2 的内容, 麻烦
于是与 co 库
- // co 库 npm install co 可以自动的将 generator 进行迭代
- let co = require('co');
- co(gRead()).then(function (data) {
- console.log(data)
- })
- // 这样就可以一次拿到结果
可是 co 库是在么实现的呢? 让我们来模拟一个 co 库
- function co(it) {
- // 必须返回一个 promise
- return new Promise(function (resolve, reject) {
- function next(d) { // 第二次递归进来就有值了
- let { value, done } = it.next(d);
- // value 是一个 promise, 第一次调用传递值无用
- if (!done) {
- // 如果没有遍历结束
- value.then(function (data) {
- // content2 的 promise 运行, 并传入读取结果
- next(data)
- }, reject)
- } else {
- resolve(value);
- }
- }
- next();
- });
- }
可以看出 co 库就是递归调动了迭代器里的每一个 next(), 直到 done===true
async-await
async-await 可以理解为 co + generator 的语法糖用 async 来修饰函数, aysnc 需要配 await,await 只能 promise
- async function aRead(){
- try{
- let content1 = await read('./1.txt','utf8');
- let content2 = await read(content1,'utf8');
- return content2;
- }catch(e){ // 如果出错会 catch
- console.log('err',e)
- }
- }
函数调用和 promise 一样, 如下:
- // async 函数返回的是 promise,
- aRead().then(function (data) {
- console.log('print', data);
- }, function (err) {
- console.log('error', err);
- });
async/await 解决了哪些问题呢?
回调地狱
并发执行异步, 在同一时刻同步返回结果 Promise.all
解决了返回值的问题, yield 返回的要靠调用函数传入
可以实现代码的 try/catch
相关函数库
- co
- bluebird
- / blueBird
- // npm install bluebird
- let fs = require('fs');
- let bluebird = require('bluebird');
- function promisify(fn) { // promise 化 将回调函数在内部进行处理
- return function (...args) {
- return new Promise(function (resolve, reject) {
- fn(...args, function (err, data) {
- if (err) reject(err);
- resolve(data);
- })
- })
- }
- }
- function promisifyAll(obj) {
- Object.keys(obj).forEach(key => { // es5 将对象转化成数组的方法
- if (typeof obj[key] === 'function') {
- obj[key + 'Async'] = promisify(obj[key])
- }
- })
- }
- promisifyAll(fs); // 将所有的方法全部增加一个 promise 化
- fs.readFileAsync('./1.txt', 'utf8').then(function (data) {
- console.log(data);
- });
- promises-aplus-tests
来源: https://juejin.im/post/5aa730276fb9a028b77a7903