JS异步解决方案的发展流程(一)

2018-02-18  本文已影响0人  那一抹阳光_d3b9

所谓"异步",简单说就是一个任务分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段,比如,在我们烧水时可以干很多事情,当水烧开后在用水洗脸。这种不连续的执行,就叫做异步。相应地,连续的执行,就叫做同步。例如在烧水的过程中我们一直等待水烧开而不去干别的事情。

1.高阶函数

函数作为一等公民,可以作为参数和返回值
高阶函数至少满足以两个条件

1.1 预置参数

let isType = function(type,obj){
    return Object.prototype.toString.call(obj) ===`[object ${type}]`
}
console.log(isType('Object',{}));
console.log(isType('Object',{}));
console.log(isType('String','hello'));
console.log(isType('String','hello'));
// 我们发现每次调用isType都需要传入类型,所以我们可以先批量产出可供调用的函数
let isType = function(type){
    return function(obj){
        return Object.prototype.toString.call(obj) ===`[object ${type}]`
    }
}
let isObject = isType('Object');
let isString = isType('String');
console.log(isObject({}));
console.log(isObject({}));

1.2 预置函数

function after(times,cb){
    return function(){
        if(--times === 0){
            cb();
        }
    }
}
let eat = after(3,function(){console.log('吃完了')});
eat();
eat();
eat();
// 当调用次数达到我们预置的次数时,执行我们预置的函数

到此我们对函数又有了进一步的认识,下面我们就来开始进入异步的解决方案。

2.回调函数

所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。我们介绍一个常见的node中的异步方法readFile可以用来读取文件。

fs.readFile(filename, function (err, data) {
  if (err) throw err;
  console.log(data);
});

在node中回调函数的第一个参数是错误对象(error-first callbacks)

2.1 回调函数的应用

function read(callback){
    setTimeout(function(){
        let result = 'zpfx';
        callback(result);
    })
}
read(function(data){
    console.log(data);
});

我们可以利用回调函数来解决异步问题

3.回调的问题

4.Promise

Promise本意是承诺,在程序中的意思就是承诺我过一段时间后会给你一个结果。 什么时候会用到过一段时间?答案是异步操作,异步是指可能比较长时间才有结果的才做,例如网络请求、读取本地文件等

4.1 Promise的三种状态

例如媳妇说想买个包,这时候他就要"等待"我的回复,我可以过两天买,如果买了表示"成功",如果我
最后拒绝表示"失败",当然我也有可能一直拖一辈子

then 方法就是用来指定Promise 对象的状态改变时确定执行的操作,resolve 时执行第一个函数
(onFulfilled),reject 时执行第二个函数(onRejected)

4.2 构造Promise

promise的方法会立刻执行

let promise = new Promise(() => {
    console.log('hello');
}); 
console.log('world'); 
// hello
// world

4.3 promise也可以代表一个未来的值

const fs = require('fs');
let  promise = new Promise((resolve, reject) => {
    fs.readFile('./content.txt', 'utf8', function (err, data)  {
        if (err) return reject(err); 
        resolve(data);
    })
 }); 
promise.then(data => {
    console.log(data);  
});
promise.then(data => {
    console.log(data);
});
// 一个promise实例可以多次调用then当成功后会将结果依次执行

4.4 代表一个用于不会返回的值

const fs = require('fs');
let  promise = new Promise((resolve, reject) => { });
promise.then(data => {
    console.log(data); //代表一个用于不会返回的值
}); 

4.5 实现买包的案例

function buyPack()  {
    return new Promise((resolve, reject) => {
        setTimeout(function  ()  {
            var  random = Math.random();
            if (random > 0.5) {
                resolve('买');
            } else {
                resolve('不买');
            }
        }, 2000)
    })
}
buyPack().then(data => {
    console.log(data);
}, data => {
    console.log(data);
}); 

4.6 Error会导致触发Reject

可以采用then的第二个参数捕获失败,也可以通过catch函数进行

function buyPack()  {
    return new Promise((resolve, reject) => {
        throw new Error('没钱')
    })
}
buyPack().then(data => {
    console.log(data);
}, data => {
    console.log(data);
}); 

5.解决回调地狱

回归正题,先用promise解决第一个问题"回调地狱"

// 1.txt => 2.txt
// 2.txt => 我很帅
let fs = require('fs');
function read(){
    fs.readFile('./1.txt','utf8',function(err,data){
        if(err) return console.log(err);
        fs.readFile(data,'utf8',function(err,data){
            if(err) return console.log(err);
            console.log(data); // 我很帅
        })
    })
}
read();

改写Promise形式

let fs = require('fs');
function read(file){
    return new Promise(function(resolve,reject){
        fs.readFile(file,'utf8',function(err,data){
            if(err) return reject(err);
            resolve(data);
        })
    })
}
read('./1.txt').then(function(data){
    return read(data);
}).then(function(data){
    console.log(data)
}).catch(function(err){
    console.log(err)
});

当第一个then中返回一个promise,会将返回的promise的结果,传递到下一个then中。这就是比较著名的链式调用了。

6.同步异步的结果

我们将多个异步请求的结果在同一时间进行汇总

// 1.txt => template
// 2.txt => data
let fs = require('fs');
let result = {}
function out(key,data) {
    result[key] = data;
    if(Object.keys(result).length === 2){
        console.log(result)
    }
}
fs.readFile('./1.txt', 'utf8', function (err, data) {
    if (err) return console.log(err);
    out('template',data);
})
fs.readFile('./2.txt', 'utf8', function (err, data) {
    if (err) return console.log(err);
    out('data',data);
});
// 这种方式并不好,要声明全局的对象,并且成功的数量也是写死的,我们可以使用偏函数来进行改写
let fs = require('fs');
let result = {}
function after(times,cb) {
    let result = {}
    return function(key,data){
        result[key] = data;
        if(Object.keys(result).length === times){
            cb(result)
        }
    }
}
let out = after(2,function(data){
    console.log(data)
})
fs.readFile('./1.txt', 'utf8', function (err, data) {
    if (err) return console.log(err);
    out('template',data);
})
fs.readFile('./2.txt', 'utf8', function (err, data) {
    if (err) return console.log(err);
    out('data',data);
});

最后我们可以采用Promise.all方法来进行简化

let fs = require('fs');
function read(file){
    return new Promise(function(resolve,reject){
        fs.readFile(file,'utf8',function(err,data){
            if(err) return reject(err);
            resolve(data);
        })
    })
}
Promise.all([read('1.txt'),read('2.txt')]).then(([template,data])=>{
    console.log({template,data})
});
// 不管两个promise谁先完成,Promise.all 方法会按照数组里面的顺序将结果返回

7.Promise.race

接受一个数组,数组内都是Promise实例,返回一个Promise实例,这个Promise实例的状态转移取决于参数的Promise实例的状态变化。当参数中任何一个实例处于resolve状态时,返回的Promise实例会变为resolve状态。如果参数中任意一个实例处于reject状态,返回的Promise实例变为reject状态。

Promise.race([read('1.txt'),read('2.txt')]).then(data=>{
    console.log({template,data})
},(err)=>{
    console.log(err)
});

8.Promise.resolve

返回一个Promise实例,这个实例处于resolve状态

Promise.resolve('成功').then(data=>{ 
    console.log(data);
});

9.Promise.reject

返回一个Promise实例,这个实例处于reject状态

Promise.reject('失败').then(data=>{ 
   console.log(data); 
},err=>{ 
console.log(err); 
}) 

下一节我们会继续介绍generator和async/await的用法,以及Promise的实现原理,喜欢的点个赞吧_!
支持我的可以给我打赏

打赏
上一篇下一篇

猜你喜欢

热点阅读