promise是什么
- 首先promise是一种解决异步方案
- Promise-ES6
- 获取数据 koa generator async await axios redux-saga fetch 都是基于promise
- Promise是一种异步流程的控制手段
为什么存在promise
针对在开发中异步需求,例如
//同步异步请求的需求
function request(content){
setTimeout((){
document.body.append(content)
},Math.floor(Math.random()*1000))
}
request('hello')
request('world')
document.body.append('finished');
//回调地狱
$.ajax(
...
sucess:{
$.ajax(
...
sucess:{
$.ajax(
...
sucess:{
}
)
}
)
}
)
假设我在读取到1.txt文件之后,读取2.txt文件,一般我们会嵌套回调,在我们有多个连续读取文件时,就会形成类似于金字塔结构的回调,我们称之为回调地狱。如果有异步挂掉,我们很难排除错误,会使得很不好维护,而且不直观。我们产生了很多种解决异步的方法
接下来我们将会介绍如下
- 高阶函数,函数是一等公民,函数可以当作参数传给另一个函数,也可以返回函数,例如,偏函数,函数柯里化 ;callback函数
- Promise
- generator + co 基于Promise
- async + await 让promise看起来更像同步
(终极)(写起来越来越像同步代码)
(promise的链式调用和JQuery的不同,他返回的不是this,而是一个新的promise) Promise类上提供类很多方法,
高阶函数
函数可以当作参数传递,典型的callback 像loadash库中的after都是这个样子
那么他是怎么实现的呢?
假设我们吃饭,吃饭是一个函数,调用3次后,在执行另一个函数,代码如下:
function after(times,callback){
return function(){
if(--times === 0){
callback()
}
}
}
let eat = after(3,function(){
console.log('吃完了')
})
输出结果:
promise
promise是怎么来的呢?为什么会有promise?,假设我要读取两个文件,我们一般会这么写:
let fs = require('fs');// fileSystem
fs.readFile('1.txt','utf8',function(err,data){
if(err) return console.log(err);
console.log(data)
fs.readFile('2.txt','utf8',function(err,data){
if(err) return console.log(err);
console.log(data)
})
})
那么这两个熟从顺序执行,如果我们想同时读取两个文件,又想让两个文件见分别异步读取,那么我们怎么判断读取完毕呢?像上面的做法肯定不行,我们可以如下
let fs = require('fs');// fileSystem
let arr = [];
fs.readFile('1.txt','utf8',function(err,data){
if(err) return console.log(err);
out(data)
})
fs.readFile('2.txt','utf8',function(err,data){
if(err) return console.log(err);
out(data)
})
function out(d){
arr.push(d);
if(arr.length == 2) console.log(arr)
}
这样的话我们在外部声明了一个变量,我们希望arr放到里面,在达到某种条件在执行这个方法,我们可以更具刚开始讲的after进一步优化代码如下:
let fs = require('fs');// fileSystem
fs.readFile('1.txt','utf8',function(err,data){
if(err) return console.log(err);
out(data)
})
fs.readFile('2.txt','utf8',function(err,data){
if(err) return console.log(err);
out(data)
})
function after(times,callback){
let arr = [];
return function(d){
arr.push(d);
if(arr.length == times) callback(arr)
}
}
let out = after(2,function(data){
console.log(data)
})
ok,用promise如何实现?具体请参考promise/A+规范,下面我们慢慢介绍:
- promise 是个一个内置类,在promise里面由一个excutor执行器,默认new式直接调用,执行器有两个参数,resolve,和reject
- 每个promise实例上都有then方法,每个then方法都有成功的函数和失败的函数
- promise发生错误就走失败态,什么时候成功,什么时候失败,取决于你什么时候调用这个函数,
- 当然我们每次执行,只能调用其中一个函数,所以promise大致结构已经出来如下:
var Promise = require('./promise.js')
let p = new Promise((resolve,reject)=>{
resolve();
})
p.then((data)=>{
console.log('s',data);
},(err)=>{
console.log('e',err);
})
promise.js
class Promise{
constructor(executor){ //executor执行器
this.status = 'pending';//默认会有三个状态
this.value = undefined;
this.reason = undefined;
let resolve = (data)=> {
if(this.status == 'pending'){//判定只有pending状态可以改变状态
this.status = 'resolved';//成功之后改变状态
this.value = data;
}
}
let reject = (reason)=> {
console.log(reason+'2')
if(this.status == 'pending'){
this.status = 'rejected';//失败之后改变失败状态
this.reason = reason;
}
}
try{//执行时可能会发生异常
executor(resolve,reject)//默认new调用,有两个函数作为参数
}catch(e){//如果捕获到异常直接走失败态
reject(e)
}
}
then(onFullFiled,onRejected){//then 里面由两个状态
if(this.status === 'resolved'){
console.log(333)
onFulFilled(this.value)//成功将成功的参数传进去
}
if(this.status === 'rejected'){
onRejected(this.reason)//失败将失败的参数传进去
}
}
}
module.exports = Promise;
- 在Promise里会有很多种情况例如Promise实例的函数是异步的
let p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('123');
},1000)
})
- 此时我们的状态还没有改变,then的时候status = > pending 状态,此时我们需要保留当前函数,当状态改变的时候在执行,我们知道,promise可能会有n个实例,可以then多次,所以当前存放成功回调和失败回调函数方法的一定是数组,我们在then里多加一个pendding状态,这里利用发布订阅思想,代码如下,
class Promise{
constructor(executor){
...
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
...
}
...
}
then(onFullFiled,onRejected){//then 里面由两个状态
...
if(this.status === 'pending'){//当前既没有完成也没有失败
//我们可以现将当前两个函数存起来,当执行完成后执行当前两个函数
this.onResolvedCallbacks.push(()=>{
onFulFilled(this.value)
})
this.onRejectedCallbacks.push(()=>{
onRejected(this.reason)
})
}
}
}
module.exports = Promise;
- then可以链式调用,如果第一个then报错,就返回失败态,走到下一个then的失败里去;如果第一个then是成功的,就会返回成功态,就走到下一个then的成功里去,并且将返回结果传过去。那么我们推测
- 每次then之后返回的都是一个promise
- 返回成功态,就走到下一个then的成功里去,并且将返回结果传过去
- 第一个then报错,就返回失败态,走到下一个then的失败里去并且将返回结果传过去
- 如返回的是普通值,直接把值作为外层的下一次then的函数
- 由于这里的情况较多,且复杂,我会尽力标注清除,以js备注为准
代码如下
class Promise{
constructor(executor){...}
then(onFulFilled,onRejected){//then 里面由两个状态
let promise2;
if(this.status === 'resolved'){
promise2 = new Promise((resolve,reject)=>{//调用then后返回一个新的promise
let x = onFulFilled(this.value)//成功将成功的参数传进去
resolvePromise(promise2,x,resolve,reject);
})
return promise2;
//每次return 的值我们要对 这个值做处理,重点就在于这个值x有很多种情况,x可能是值,function,obj,等,我们要对这个作出分析,来判定我们即将要做什么,因为在then的每一种情况,我们都要处理这种关系,所以我们定义一个函数,来检查并继续相应的操作;
//=> 如果x和promise2是同一个对象,那么会一直处于等待中,我们会跑出一个类型错误
//=> 如果是 值 直接resolve(x);
//=> 如果是 function或者 Object 我们认为这就是个promise,返回结果作为外层下次then的函数
// 这个promise也会分几种情况
//Object.defineProperty(x,'then',{
//get(){
//throw new Error()
// },
// set(){
// }
//})
// ===> 如果用户通过defineProperty,使x的值弹出错误,我们需要捕获错误
// ===> 如果是 值
// ===> 如果是 值
}
if(this.status === 'rejected'){...}
//既没有成功也没有失败
if(this.status === 'pending'){//当前既没有完成也没有失败
promise2 = new Promise((resolve,reject)=>{...})
return promise2;
}
}
module.exports = Promise;
- 根据以上总结我们来写一下resolvePromise()的实现
- 如果promise2和x引用同一个object,会抛出一个引用类型错误
//解析promise和promise2的关系
function resolvePromise(promise2,x,resolve,reject){
//判断x是不是promise
//规范里规定来一段代码,这个代码规定我的promise可以和别人的promise可以进行交互
//如果promise2和x引用同一个object,会抛出一个引用类型错误
if(promise2 === x){//不能自己等待自己完成
throw reject(new TypeError('循环引用'));
}
//如果x是一个对象或者函数(object/function)有可能是promise
if(x !== null && (typeof x === 'object' ||typeof x === 'function')){//type of null === Object
try{//如果用户通过defineProperty,防止then异常
let then = x.then ;//取x的then {then:{}} 29S
if(typeof then === 'function'){//如果then是函数,就认为他是个promise,
then.call(x,y => { //resolve的结果可能依旧是promise,,要继续递归,解析promise
resolvePromise(promise2,y,resolve,reject);
},err => {
reject(err)//只要失败就失败了
})//call(x)相当于让then执行,
}else{//如果不是函数就是普通值
resolve(err)//then是一个普通对象,成功即可
}
}catch(e){
reject(e)
}
}else{//否则结果是个普通的值,直接进入成功态
resolve(x)
}
}
测试
let Promise = require('./a.js');
let p = new Promise((resolve,reject)=>{
resolve(123)
})
var p2 = p.then((data) => {
return p2 //返回的promise既不可能成功也不会失败
})
p2.then((data) => {
console.log('p1data',data);
},(err)=> {
console.log(err)
})
测试
let Promise = require('./a.js');
let p = new Promise((resolve,reject)=>{
resolve(123)
})
p.then( data => {
return new Promise((resolve,reject)=>{
resolve(new Promise((resolve,reject)=>{
resolve(100)
}))
})
},err => {
console.log('p',err);
}).then((data)=>{
console.log('sf',data);
},(err)=>{
console.log('perr',err);
})
测试结果
经过测试基本功能已经实现,但是如果成功就不能失败,失败就能成功,我们怎么限制他只能调一次呢?我们可以加个参数
function resolvePromise(promise2, x, resolve, reject) {
// 判断x是不是promise
// 规范里规定了一段代码,这个代码可以实现我们的promise和别人的promise可以进行交互
if (promise2 === x) { // 不能自己等待自己完成
return reject(new TypeError('循环引用'));
}
// x不是null或者是对象或者函数
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
let called; // 防止成功后调用失败
try { // 防止取then是出现异常 Object.defineProperty
let then = x.then; // 取x的then方法 {then:{}}
if (typeof then === 'function') { // 如果then是函数我就认为它是promise
// call 第一个参数是this ,后面的是成功的回调和失败的回调
then.call(x, y => { // 如果y是promise就继续递归解析promise
if(called) return;
called = true;
resolvePromise(promise2,y,resolve,reject);
}, r => { // 只要失败了就失败了
if (called) return;
called = true;
reject(r);
});
}else{ // then是一个普通对象,就直接成功即可1
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else { // x = 123
resolve(x); // x就是一个普通值
}
}
那如果then里面什么也没写,我们需要做一个值的穿透
p.then().then((y) => {
},e => {
console.log(e)
})
js
p.then().then((y) => {
},e => {
console.log(e)
})
当我们的实例中放的是异步代码
then(onFulFilled,onRejected){//then 里面由两个状态
//解决onfullFilled不传值的问题
onFulFilled = typeof onFulFilled === 'function'? onFulFilled:y => y;
onRejected = typeof onRejected === 'function'? onRejected: err => {
throw err;//不能new
};
...
}
但是这样的话,reject会走到成功,所以在reject的时候我们应该抛出错误,而且值是异步的,我们需要try,catch捕获,excutor执行的时候我们try catach,但是内容是异步的就无法捕获错误,所以我们需要给每个then中的方法,都加try,catch
then(onFulFilled,onRejected){//then 里面由两个状态
//解决onfullFilled不传值的问题
onFulFilled = typeof onFulFilled === 'function'? onFulFilled:y => y;
onRejected = typeof onRejected === 'function'? onRejected:err => {
throw err;//不能new
};
let promise2;
if(this.status === 'resolved'){
promise2 = new Promise((resolve,reject)=>{//调用then后返回一个新的promise
try{
let x = onFulFilled(this.value)//成功将成功的参数传进去
//看x是不是promise,如果是,取结果,作为promise2成功的结果
//如返回的是普通值作为promise2成功的结果
//如果成功或者失败,promise2的成功或者失败会被调用
//这里我们对以上做统一处理
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e)
}
})
return promise2;
}
if(this.status === 'rejected'){
promise2 = new Promise((resolve,reject)=>{
try{
let x = onRejected(this.reason)//失败将失败的参数传进去
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e)
}
})
return promise2;
}
//既没有成功也没有失败
if(this.status === 'pending'){//当前既没有完成也没有失败
promise2 = new Promise((resolve,reject)=>{
//我们可以现将当前两个函数存起来,当执行完成后执行当前两个函数
this.onResolvedCallbacks.push(()=>{
try{
let x = onFulFilled(this.value);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e)
}
})
this.onRejectedCallbacks.push(()=>{
try{
let x = onRejected(this.reason);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e)
}
})
})
return promise2;
}
}
我们知道promise中的then是异步的,而且他是微任务,但是现实中,浏览器有例如setTimeout这样异步的宏任务,可以考虑用,所以我们这次暂时用setTimeout实现异步:
then(onFulFilled,onRejected){//then 里面由两个状态
//解决onfullFilled不传值的问题
onFulFilled = typeof onFulFilled === 'function'? onFulFilled:y => y;
onRejected = typeof onRejected === 'function'? onRejected:err => {
throw err;//不能new
};
let promise2;
if(this.status === 'resolved'){
promise2 = new Promise((resolve,reject)=>{//调用then后返回一个新的promise
setTimeout(() => {
try{
let x = onFulFilled(this.value)//成功将成功的参数传进去
//看x是不是promise,如果是,取结果,作为promise2成功的结果
//如返回的是普通值作为promise2成功的结果
//如果成功或者失败,promise2的成功或者失败会被调用
//这里我们对以上做统一处理
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e)
}
},0)
})
}
if(this.status === 'rejected'){
promise2 = new Promise((resolve,reject)=>{
setTimeout(() => {
try{
let x = onRejected(this.reason)//失败将失败的参数传进去
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e)
}
},0)
})
}
//既没有成功也没有失败
if(this.status === 'pending'){//当前既没有完成也没有失败
promise2 = new Promise((resolve,reject)=>{
//我们可以现将当前两个函数存起来,当执行完成后执行当前两个函数
this.onResolvedCallback.push(()=>{
setTimeout(() => {
try{
let x = onFullFilled(this.value);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e)
}
},0)
})
this.onRejectedCallbacks.push(()=>{
setTimeout(() => {
try{
let x = onRejected(this.reason);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e)
}
},0)
})
})
}
return promise2;
}
这样我们的Promise类就完成了
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('循环引用'));
}
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
let called;
try { // 防止取then是出现异常 Object.defineProperty
let then = x.then; // 取x的then方法 {then:{}}
if (typeof then === 'function') { // 如果then是函数我就认为它是promise
// call 第一个参数是this ,后面的是成功的回调和失败的回调
then.call(x, y => { // 如果y是promise就继续递归解析promise
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, r => { // 只要失败了就失败了
if (called) return;
called = true;
reject(r);
});
} else { // then是一个普通对象,就直接成功即可1
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else { // x = 123
resolve(x); // x就是一个普通值
}
}
class Promise {
constructor(executor) {
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (data) => {
if (this.status === 'pending') {
this.value = data;
this.status = 'resolved';
this.onResolvedCallbacks.forEach(fn => fn());
}
}
let reject = (reason) => {
if (this.status === 'pending') {
this.reason = reason;
this.status = 'rejected';
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulFilled, onRejected) {
onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : y => y;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };
let promise2;
if (this.status === 'resolved') {
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onFulFilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
if (this.status === 'rejected') {
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
});
}
if (this.status === 'pending') {
promise2 = new Promise((resolve, reject) => {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulFilled(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0)
});
// 存放失败的回调
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
})
}
return promise2; // 调用then后返回一个新的promise
}
}
module.exports = Promise;
测试
let Promise = require('./c.js');
let p = new Promise((resolve,reject)=>{
resolve(123)
})
p.then( data => {
console.log('qw',data)
},err => {
console.log('p',err);
}).then((data)=>{
console.log('sf',data);
},(err)=>{
console.log('perr',err);
})
结果
假设我们读取一个文件,代码如下:
let Promise = require('./a.js');
let fs = require('fs')
function read(){
return new Promise((resolve,reject)=>{
fs.readFile('./a.js','utf8',(err,data)=>{
if(err) reject(err);
resolve(data);
})
})
}
这样看还是有点繁琐,在promise里面有个语法糖
promise.deferred是一个语法糖,属于promise的一部分
Promise.deferred = Promise.defer = function{
let dfd = {};
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
为什么叫语法糖呢?有来这个属性之后我们可以把读文件的代码这样写
let Promise = require('./a.js');
let fs = require('fs')
function read(){
let deffer = Promise.deffer();
fs.readFile('./a.js','utf8',(err,data)=>{
if(err) deffer.reject(err);
deffer.resolve(data);
})
return deffer.promise;//保持他的then功能
}
可以帮我解决其中一层嵌套问题,但是对于错误处理不太方便了就。 这样我们创建一个基础的Promise,下面我们做一下
验证
需要npm install promise-aplus-tests -g 安装,然后在当前文件下npm promise-aplus-test 文件名 测试
当然Promise 也有很多扩展方法,像promise.all, catch(),promise.race(),promise.resolve(),promise.reject(),我们来看看他的这些扩展方法是如何实现的
- catch() catch其实是then的简写,只是没有成功的回调,可以统一处理错误
then() =>{
...
}
catch(onrejected){
return this.then(null,rejected)
}
测试
let Promise = require('./a.js');
let p = new Promise((resolve,reject)=>{
reject('err');
})
p.then().then().catch(r=>{
console.log(r)
}).then(data=>{console.log('data',data)})
[Running] node "/Users/myloveyunyun/Desktop/node/worker.js"
err
data undefined
- promise.resolve() && promise.reject()
Promise.resolve = function (val) {
return new Promise((resolve, reject) => {
resolve(val);
})
}
Promise.reject = function (val) {
return new Promise((resolve, reject) => {
reject(val);
})
}
测试
let Promise = require('./a.js');
Promise.resolve("hello").then(err=>{
console.log('data',err)
})
Promise.reject("qweerr").then().then(data=>{
console.log('data',data
)},err =>{
console.log('err',err)
})
[Running] node "/Users/myloveyunyun/Desktop/node/worker.js"
data hello
err qweerr
- promise.all() && promise.race()
Promise.all = function (promises) {//数组里面是并发状态,有一个失败,就失败了
return new Promise((resolve, reject) => {
let i = 0;
let arr = [];
function processData(index,data){
arr[index] = data;
i++;
if(i === promises.length){
resolve(arr);
}
}
for(let i=0;i<promises.length;i++){
promises[i].then(data =>{
processData(i,data)
},reject)
}
})
}
//和all对应的是race赛跑,,以最快的为结果
Promise.race = function(promises){
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
promises[i].then(resolve,reject)
}
})
}
测试
let Promise = require('./a.js');
let fs = require('fs');
function read(url){
let defer = Promise.defer();
fs.readFile(url,'utf8',(err,data)=>{
if(err) defer.reject(err);
defer.resolve(data);
})
return defer.promise;
}
Promise.all([
read('./1.txt'),
read('./2.txt')
]).then(arr=>{
console.log('as' ,arr)
},err=>{
console.log('sad',err)
})
Promise.race([
read('./1.txt'),
read('./2.txt')
]).then(arr=>{
console.log('as' ,arr)
},err=>{
console.log('sad',err)
})
[Running] node "/Users/myloveyunyun/Desktop/node/worker.js"
as [ 'hahahaahah', '\'我很帅\'' ]
as hahahaahah
bluebird && Q,支持语法糖Q库请参考
可以去git上查看
用法
命令行,在当前文件夹 npm install bluebird
let bluebird = require('bluebird')
let fs = require('fs')
//function read(url){
// let defer = Promise.defer();
// fs.readFile(url,'utf8',(err,data)=>{
// if(err) defer.reject(err);
// defer.resolve(data);
// })
// return defer.promise;
//}
//相比之前要创建的promise,使用bluebird可以帮我们把一步方法转换成promise方法
let read = bluebird.promisify(fs.readFile);
read('./2.txt','utf8').then(data=>{
console.log(data)
})
[Running] node "/Users/myloveyunyun/Desktop/node/worker.js"
'我很帅'
那么他是如何实现的呢?
function promisify(fn){//promisify会给fn加上一个回调
return function(...arg){
return new Promise((resolve,reject)=>{
fn(...arg,fnunction(err,data){
if(err) resolve(err);
reject(data);
})
})
}
}
function promisifyAll(obj){//全部promise化
for(let key in obj){
if(typeof obj[key] === 'function'){
obj[key+'Async'] = promisify(obj[key])
}
}
}
基于promise 的generator和co的实现
generator 生成器 -> 生成的迭代器 ES6提供
let obj = {0:1,1:2,length:2,[symbol.iter]:function(){ return {}}}
- [symbol.iter]后面的函数叫迭代函数
- 迭代器函数会返回一个对象,
- 返回的对象中必须返回
next方法,,然后返回value,done - generator相当于把一个函数分成若干个部分执行,执行第一次时,将指针指向下一段代码,直到结束为止,,,
- 如果要在generator中调用另一个 gen() == {} 指向的是迭代器 ,需要用yield* gen()执行
let obj = {
0:1,
1:2,
length:2,
[symbol.iter]:
function(){
return {
let index = 0;
let that = this;
return{
next(){
value:that[index],//值
done:index++ === that.length //done 代表是否迭代完成
}
}
}
}
}
let arr = [...obj];
console.log(arr);
测试
function read(arr){
let index = 0;
return{
next(){
return {
value:arr[index],
done:++index === arr.length
}
}
}
}
let it = read(['vue','react','node']);
console.log(it.next())
console.log(it.next())
console.log(it.next())
[Running] node "/Users/myloveyunyun/Desktop/node/worker.js"
{ value: 'vue', done: false }
{ value: 'react', done: false }
{ value: 'node', done: true }
我们同样可以用生成器函数实现
function * gen(){ //* 和 yield一起使用,产出
yield 1000;
yield 3000;
}
let a = gen();
console.log(a.next())
console.log(a.next())
[Running] node "/Users/myloveyunyun/Desktop/node/worker.js"
{ value: 1000, done: false }
{ value: 3000, done: false }
function * gen(){ //* 和 yield一起使用,产出
return 1000;
}
let a = gen();
console.log(a.next())
[Running] node "/Users/myloveyunyun/Desktop/node/worker.js"
{ value: 1000, done: true }
- 这里我们要注意的是第一次next的参数没有任何意义,下一次next的参数是上一次yiled的返回值,我们这么写到底有什么用呢? 一般我们会和promise结合
let {promisify} = require('bluebird');
let fs = require('fs');
let read = promisify(fs.readFile);
let co = require('co');
function * gen(){
let b = yield read('1.txt','utf8');
let c = yield read('2.txt','utf8');
return c
}
let it = gen();
it.next().value.then(data=>{
it.next(data).value.then(data=>{
console.log(it.next(data).value)
})
})
我们可能会n多层嵌套,,有什么更好的方法,,Co 安装npm i co -g 他可以帮我们执行promise,我们可以讲带代码优化如下:
let co = require('co');
co(gen()).then(data=>{
console.log(data)
})
co的实现
function co(it){
//递归
return new Promise((resolve,reject){
function next(){//实现异步迭代
let {value,done} = it.next(data);
if(!done){
value.then(data => {
//当第一个执行完执行下一个then
next(data)
}) ,reject);//有一个失败就失败了
}else{
resolve(value)
}
}
})
}
async + await 是generator的语法糖
- async返回的结果是promise
async function r(){
let b = await read('a.txt','utf8');
let c = await read('b.txt','utf8');
return b;
}
r().then(data=>{})
原理
function _asyncToGenerator(fn){ // co(gen())
return function(){
var gen = fn.appaly(this,arguments); //gen() == it
return new Promise(function(resolve,reject){
function step(key,arg){
try {
var info = gen[key](arg){
var value = info.value;
}catch(error){
reject(error);
return
}
if(info.done){
resolve(vakue);
}else{
return Promise.resolve(value).then(function(value){
step('next',value)//next
}),function(err){
step('throw',err)
})
}
}
})
}
}
promise 可以支持多个并发请求,获取并发请求中的数据,我们要理解promise 本身是 同步的,但是他的then方法是异步的。 以上是我们对Promise的简单介绍,我们通过一个小例子来使我们的内容更加清晰
移动小球 一分钟学会所有异步实现
需求,id ball1,2,3 3个球第一个球移动后,下一个球移动
- 回调函数方法
function move(ele,position,cb){
let left = 0;
let timer = setInterval(() => {
left += 5;
if(left >= positon){
clearaInterval(timer)
ele.style.transform = `translate(${left}px)`
}else{
ele.style.transform = `translate(${left}px)`
}
},15)
}
move(ball1,500,function(){
move(ball2,500,function(){
move(ball3,500,function(){
alergt('动完了')
})
})
})
- promise
function move(ele,position){
return new Promise((resolve,reject) => {
let left = 0;
let timer = setInterval(() => {
left += 5;
if(left >= positon){
clearaInterval(timer)
ele.style.transform = `translate(${left}px)`;
resolve();
}else{
ele.style.transform = `translate(${left}px)`
}
},15)
})
}
move(ball1,500).then(data=>{return move(ball2,500)}).then(data=>{return move(ball3,500)}).then(alert('ok'))
- generator+co
function * m(){
yield move(ball1,500);
yield move(ball2,500);
yield move(ball3,500);
}
function co(it){
return new Promise((resolve,reject)=>{
function next(done,value){
let {done,value} = it.next();
if(done) return resolve(value)
value.then(data=>{
next(data)
},reject)
}
next();
})
}
co(m()).then(data=>{
alert('ok')
})
- async+await
async function m(){
await move(ball1,500);
await move(ball2,500);
await move(ball3,500);
}
m().then(data=>{
alert('ok')
})