在学习 Node.js 过程中接触到了如何使用 async 来控制并发(使用 async 控制并发)

async 的本质是一个流程控制。其实在异步编程中,还有一个更为经典的模型,叫做 Promise/Deferred 模型(当然还有更多相关解决方法,比如 eventproxy,co 等,到时候遇到在挖坑)

首先,我们思考一个典型的异步编程模型,考虑这样一个题目:读取一个文件,在控制台输出这个文件内容

var fs = require('fs');
fs.readFile('1.txt', 'utf8', function (err, data) {
  console.log(data);
});

看起来很简单,再进一步: 读取两个文件,在控制台输出这两个文件内容

var fs = require('fs');
fs.readFile('1.txt', 'utf8', function (err, data) {
  console.log(data);
  fs.readFile('2.txt', 'utf8', function (err, data) {
    console.log(data);
  });
});

要是读取更多的文件呢

var fs = require('fs');
fs.readFile('1.txt', 'utf8', function (err, data) {
  fs.readFile('2.txt', 'utf8', function (err, data) {
    fs.readFile('3.txt', 'utf8', function (err, data) {
      fs.readFile('4.txt', 'utf8', function (err, data) {
        // ...
      });
    });
  });
});

这就是传说中的 callback hell,可以使用 async 来改善这段代码,但是在本例中我们要用 promise/defer 来改善它

promise 基本概念

首先它是一个对象,它和 javascript 普通的对象没什么区别,同时,它也是一种规范,跟异步操作约定了统一的接口,表示一个异步操作的最终结果,以同步的方式来写代码,执行的操作是异步的,但又保证程序执行的顺序是同步的

1. promise 只有三种状态,未完成,完成 (fulfilled) 和失败 (rejected)

2. promise 的状态可以由未完成转换成完成,或者未完成转换成失败

3. promise 的状态转换只发生一次

promise 有一个 then 方法,then 方法可以接受 3 个函数作为参数。前两个函数对应 promise 的两种状态 fulfilled, rejected 的回调函数。第三个函数用于处理进度信息

为了理解它,一些重要原理必须记牢:.then() 总是返回一个新的 promise,如下面代码:

var promise = readFile()
var promise2 = promise.then(readAnotherFile, console.error)

这里 then 的参数 readAnotherFile, console.error 是代表异步操作成功后的动作 onFulfilled 或失败后的动作 OnRejected,也就是说,读取文件成功后执行 readAnotherFile 函数,否则失败打印记录错误。这种实现是两个中只有一种可能

也可以理解为:

promiseSomething().then(function (fulfilled) {
  // 当 promise 状态变成 fulfilled 时,调用此函数
}, function (rejected) {
  // 当 promise 状态变成 rejected 时,调用此函数
}, function (progress) {
  // 当返回进度信息时,调用此函数
});

Promise 法则有两部分必须分离:

1. then() 总是返回一个新的 promise,每次你调用它,它不管回调做什么,因为 .then() 在回调被调用之前已经给了你一个承诺 promise,回调的行为只影响承诺 promise 的实施,如果回调返回一个值,那么 promise 将使用那个值,如果这个值是一个 promise,返回这个 promise 实施后的值给这个值,如果回调抛出错误,promise 将拒绝错误

2. 被 .then() 返回的 promise 是一个新的 promise ,它不同于那些 .then() 被调用的 promise,promise 长长的链条有时会好些隐藏这个事实,不管如何,每次 .then() 调用都会产生一个新的 promise,这里必须注意的是你真正需要考虑的是你最后调用 .then() 可能代表失败,那么如果你不捕获这种失败,那么容易导致你的错误 exception 消失

来看一个利用 q 来处理这种问题的简单例子:

var Q = require('q');
var defer = Q.defer();
/**
 * 获取初始 promise
 * @private
 */
function getInitialPromise() {
  return defer.promise;
}
 
/**
 * 为 promise 设置三种状态的回调函数
 */
getInitialPromise().then(function (success) {
  console.log(success);
}, function (error) {
  console.log(error);
}, function (progress) {
  console.log(progress);
});
defer.notify('in progress'); // 控制台打印 in progress
defer.resolve('resolve');   // 控制台打印 resolve
defer.reject('reject');    // 没有输出。promise 的状态只能改变一次

promise 的传递

then 方法会返回一个 promise,在下面这个例子中,我们用 outputPromise 指向 then 返回的 promise。

var outputPromise = getInputPromise().then(function (fulfilled) {
 
  }, function (rejected) {
 
  });

现在 outputPromise 就变成了受 function(fulfilled) 或者 function(rejected) 控制状态的 promise 了。直白的意思就是:当 function(fulfilled) 或者 function(rejected) 返回一个值,比如一个字符串,数组,对象等等,那么 outputPromise 的状态就会变成 fulfilled。

在下面这个例子中,我们可以看到,当我们把 inputPromise 的状态通过 defer.resovle() 变成 fulfilled 时,控制台输出 fulfilled.

当我们把 inputPromise 的状态通过 defer.reject() 变成 rejected,控制台输出 rejected

var Q = require('q');
var defer = Q.defer();
/**
 * 通过 defer 获得 promise
 * @private
 */
function getInputPromise() {
  return defer.promise;
}
 
/**
 * 当 inputPromise 状态由未完成变成 fulfil 时,调用 function(fulfilled)
 * 当 inputPromise 状态由未完成变成 rejected 时,调用 function(rejected)
 * 将 then 返回的 promise 赋给 outputPromise
 * function(fulfilled) 和 function(rejected) 通过返回字符串将 outputPromise 的状态由
 * 未完成改变为 fulfilled
 * @private
 */
var outputPromise = getInputPromise().then(function (fulfilled) {
  return 'fulfilled';
}, function (rejected) {
  return 'rejected';
});
 
/**
 * 当 outputPromise 状态由未完成变成 fulfil 时,调用 function(fulfilled),控制台打印 'fulfilled: fulfilled'。
 * 当 outputPromise 状态由未完成变成 rejected, 调用 function(rejected), 控制台打印 'rejected: rejected'。
 */
outputPromise.then(function (fulfilled) {
  console.log('fulfilled: '   fulfilled);
}, function (rejected) {
  console.log('rejected: '   rejected);
});
 
/**
 * 将 inputPromise 的状态由未完成变成 rejected
 */
defer.reject(); // 输出 fulfilled: rejected
 
/**
 * 将 inputPromise 的状态由未完成变成 fulfilled
 */
//defer.resolve(); // 输出 fulfilled: fulfilled

当 function(fulfilled) 或者 function(rejected) 抛出异常时,那么 outputPromise 的状态就会变成 rejected

var Q = require('q');
var fs = require('fs');
var defer = Q.defer();
 
/**
 * 通过 defer 获得 promise
 * @private
 */
function getInputPromise() {
  return defer.promise;
}
 
/**
 * 当 inputPromise 状态由未完成变成 fulfil 时,调用 function(fulfilled)
 * 当 inputPromise 状态由未完成变成 rejected 时,调用 function(rejected)
 * 将 then 返回的 promise 赋给 outputPromise
 * function(fulfilled) 和 function(rejected) 通过抛出异常将 outputPromise 的状态由
 * 未完成改变为 reject
 * @private
 */
var outputPromise = getInputPromise().then(function (fulfilled) {
  throw new Error('fulfilled');
}, function (rejected) {
  throw new Error('rejected');
});
 
/**
 * 当 outputPromise 状态由未完成变成 fulfil 时,调用 function(fulfilled)。
 * 当 outputPromise 状态由未完成变成 rejected, 调用 function(rejected)。
 */
outputPromise.then(function (fulfilled) {
  console.log('fulfilled: '   fulfilled);
}, function (rejected) {
  console.log('rejected: '   rejected);
});
 
/**
 * 将 inputPromise 的状态由未完成变成 rejected
 */
defer.reject();   // 控制台打印 rejected [Error:rejected]
 
/**
 * 将 inputPromise 的状态由未完成变成 fulfilled
 */
//defer.resolve(); // 控制台打印 rejected [Error:fulfilled]

当 function(fulfilled) 或者 function(rejected) 返回一个 promise 时,outputPromise 就会成为这个新的 promise.

这样做的意义在于聚合结果 (Q.all),管理延时,异常恢复等等

比如说我们想要读取一个文件的内容,然后把这些内容打印出来。可能会写出这样的代码:

// 错误的写法
var outputPromise = getInputPromise().then(function (fulfilled) {
  fs.readFile('test.txt', 'utf8', function (err, data) {
    return data;
  });
});

然而这样写是错误的,因为 function(fulfilled) 并没有返回任何值。需要下面的方式:

var Q = require('q');
var fs = require('fs');
var defer = Q.defer();
 
/**
 * 通过 defer 获得promise
 * @private
 */
function getInputPromise() {
  return defer.promise;
}
 
/**
 * 当 inputPromise 状态由未完成变成 fulfil时,调用 function(fulfilled)
 * 当 inputPromise 状态由未完成变成 rejected时,调用 function(rejected)
 * 将 then 返回的 promise 赋给 outputPromise
 * function(fulfilled) 将新的 promise 赋给 outputPromise
 * 未完成改变为 reject
 * @private
 */
var outputPromise = getInputPromise().then(function (fulfilled) {
  var myDefer = Q.defer();
  fs.readFile('test.txt', 'utf8', function (err, data) {
    if (!err && data) {
      myDefer.resolve(data);
    }
  });
  return myDefer.promise;
}, function (rejected) {
  throw new Error('rejected');
});
 
/**
 * 当 outputPromise 状态由未完成变成 fulfil 时,调用 function(fulfilled),控制台打印 test.txt 文件内容。
 *
 */
outputPromise.then(function (fulfilled) {
  console.log(fulfilled);
}, function (rejected) {
  console.log(rejected);
});
 
/**
 * 将 inputPromise 的状态由未完成变成 rejected
 */
//defer.reject();
 
/**
 * 将 inputPromise 的状态由未完成变成 fulfilled
 */
defer.resolve(); // 控制台打印出 test.txt 的内容

方法传递

方法传递有些类似于 Java 中的 try 和 catch。当一个异常没有响应的捕获时,这个异常会接着往下传递

方法传递的含义是当一个状态没有响应的回调函数,就会沿着 then 往下找

没有提供 function(rejected)

var outputPromise = getInputPromise().then(function (fulfilled) { })

如果 inputPromise 的状态由未完成变成 rejected, 此时对 rejected 的处理会由 outputPromise 来完成

var Q = require('q');
var fs = require('fs');
var defer = Q.defer();
/**
 * 通过defer获得promise
 * @private
 */
function getInputPromise() {
  return defer.promise;
}
 
/**
 * 当 inputPromise 状态由未完成变成 fulfil 时,调用 function(fulfilled)
 * 当 inputPromise 状态由未完成变成 rejected 时,这个 rejected 会传向 outputPromise
 */
var outputPromise = getInputPromise().then(function (fulfilled) {
  return 'fulfilled'
});
outputPromise.then(function (fulfilled) {
  console.log('fulfilled: '   fulfilled);
}, function (rejected) {
  console.log('rejected: '   rejected);
});
 
/**
 * 将 inputPromise 的状态由未完成变成 rejected
 */
defer.reject('inputpromise rejected'); // 控制台打印 rejected: inputpromise rejected
 
/**
 * 将 inputPromise的状态由未完成变成fulfilled
 */
//defer.resolve();

没有提供 function(fulfilled)

var outputPromise = getInputPromise().then(null, function (rejected) { })

如果 inputPromise 的状态由未完成变成 fulfilled, 此时对 fulfil 的处理会由 outputPromise 来完成

var Q = require('q');
var fs = require('fs');
var defer = Q.defer();
 
/**
 * 通过defer获得promise
 * @private
 */
function getInputPromise() {
  return defer.promise;
}
 
/**
 * 当 inputPromise 状态由未完成变成 fulfil时,传递给 outputPromise
 * 当 inputPromise 状态由未完成变成 rejected时,调用 function(rejected)
 * function(fulfilled) 将新的 promise 赋给 outputPromise
 * 未完成改变为 reject
 * @private
 */
var outputPromise = getInputPromise().then(null, function (rejected) {
  return 'rejected';
});
 
outputPromise.then(function (fulfilled) {
  console.log('fulfilled: '   fulfilled);
}, function (rejected) {
  console.log('rejected: '   rejected);
});
 
/**
 * 将 inputPromise 的状态由未完成变成 rejected
 */
// defer.reject('inputpromise rejected');
 
/**
 * 将 inputPromise 的状态由未完成变成fulfilled
 */
defer.resolve('inputpromise fulfilled'); // 控制台打印fulfilled: inputpromise fulfilled

可以使用 fail(function(error)) 来专门针对错误处理,而不是使用 then(null,function(error))

var outputPromise = getInputPromise().fail(function (error) { })

看这个例子:

var Q = require('q');
var fs = require('fs');
var defer = Q.defer();
/**
 * 通过defer获得promise
 * @private
 */
function getInputPromise() {
  return defer.promise;
}
 
/**
 * 当 inputPromise 状态由未完成变成 fulfil 时,调用 then(function(fulfilled))
 * 当 inputPromise 状态由未完成变成 rejected 时,调用 fail(function(error))
 * function(fulfilled) 将新的 promise 赋给 outputPromise
 * 未完成改变为reject
 * @private
 */
var outputPromise = getInputPromise().then(function (fulfilled) {
  return fulfilled;
}).fail(function (error) {
  console.log('fail: '   error);
});
/**
 * 将 inputPromise 的状态由未完成变成 rejected
 */
defer.reject('inputpromise rejected');// 控制台打印 fail: inputpromise rejected
 
/**
 * 将 inputPromise 的状态由未完成变成 fulfilled
 */
//defer.resolve('inputpromise fulfilled');

可以使用 progress(function (progress)) 来专门针对进度信息进行处理,而不是使用 then(function (success) { }, function (error) { }, function (progress) { })

var Q = require('q');
var defer = Q.defer();
/**
 * 获取初始 promise
 * @private
 */
function getInitialPromise() {
  return defer.promise;
}
/**
 * 为 promise 设置 progress 信息处理函数
 */
var outputPromise = getInitialPromise().then(function (success) {
 
}).progress(function (progress) {
  console.log(progress);
});
 
defer.notify(1);
defer.notify(2); // 控制台打印 1,2

promise 链

promise 链提供了一种让函数顺序执行的方法

函数顺序执行是很重要的一个功能。比如知道用户名,需要根据用户名从数据库中找到相应的用户,然后将用户信息传给下一个函数进行处理

var Q = require('q');
var defer = Q.defer();
// 一个模拟数据库
var users = [{ 'name': 'andrew', 'passwd': 'password' }];
function getUsername() {
  return defer.promise;
}
 
function getUser(username) {
  var user;
  users.forEach(function (element) {
    if (element.name === username) {
      user = element;
    }
  });
  return user;
}
 
// promise 链
getUsername().then(function (username) {
  return getUser(username);
}).then(function (user) {
  console.log(user);
});
 
defer.resolve('andrew');

我们通过两个 then 达到让函数顺序执行的目的。

then 的数量其实是没有限制的。当然,then 的数量过多,要手动把他们链接起来是很麻烦的。比如

foo(initialVal).then(bar).then(baz).then(qux)

这时我们需要用代码来动态制造 promise 链

var funcs = [foo, bar, baz, qux]
var result = Q(initialVal) 
funcs.forEach(function (func) {
  result = result.then(func)
})
return result

当然,我们可以再简洁一点

var funcs = [foo, bar, baz, qux]
funcs.reduce(function (pre, current),Q(initialVal){
  return pre.then(current)
})

看一个具体的例子

function foo(result) {
  console.log(result);
  return result   result;
}
 
// 手动链接
Q('hello').then(foo).then(foo).then(foo);
 
// 控制台输出: hello
// hellohello
// hellohellohello
 
// 动态链接
var funcs = [foo, foo, foo];
var result = Q('hello');
 
funcs.forEach(function (func) {
  result = result.then(func);
});
 
// 精简后的动态链接
funcs.reduce(function (prev, current) {
  return prev.then(current);
}, Q('hello'));

对于 promise 链,最重要的是需要理解为什么这个链能够顺序执行。如果能够理解这点,那么以后自己写 promise 链可以说是轻车熟路啊

promise 组合

回到我们一开始读取文件内容的例子。如果现在让我们把它改写成 promise 链,是不是很简单呢?

var Q = require('q'),
  fs = require('fs');
 
function printFileContent(fileName) {
  return function () {
    var defer = Q.defer();
    fs.readFile(fileName, 'utf8', function (err, data) {
      if (!err && data) {
        console.log(data);
        defer.resolve();
      }
    })
    return defer.promise;
  }
}
 
// 手动链接
printFileContent('sample01.txt')()
  .then(printFileContent('sample02.txt'))
  .then(printFileContent('sample03.txt'))
  .then(printFileContent('sample04.txt'));  // 控制台顺序打印 sample01 到 sample04 的内容

很有成就感是不是。然而如果仔细分析,我们会发现为什么要他们顺序执行呢,如果他们能够并行执行不是更好吗? 我们只需要在他们都执行完成之后,得到他们的执行结果就可以了

我们可以通过 Q.all([promise1,promise2...]) 将多个 promise 组合成一个 promise 返回。 注意:

1. 当 all 里面所有的 promise 都 fulfil 时,Q.all 返回的 promise 状态变成 fulfil

2. 当任意一个 promise 被 reject 时,Q.all 返回的 promise 状态立即变成 reject

我们来把上面读取文件内容的例子改成并行执行吧

var Q = require('q');
var fs = require('fs');
/**
 *读取文件内容
 *@private
 */
function printFileContent(fileName) {
  // Todo: 这段代码不够简洁。可以使用 Q.denodeify 来简化
  var defer = Q.defer();
 
  fs.readFile(fileName, 'utf8', function (err, data) {
    if (!err && data) {
      console.log(data);
      defer.resolve(fileName   ' success ');
    } else {
      defer.reject(fileName   ' fail ');
    }
  })
 
  return defer.promise;
 
}
 
Q.all([printFileContent('sample01.txt'), printFileContent('sample02.txt'), printFileContent('sample03.txt'), printFileContent('sample04.txt')])
  .then(function (success) {
    console.log(success);
  }); // 控制台打印各个文件内容 顺序不一定

现在知道 Q.all 会在任意一个 promise 进入 reject 状态后立即进入 reject 状态。如果我们需要等到所有的 promise 都发生状态后(有的 fulfil, 有的 reject),再转换 Q.all 的状态, 这时我们可以使用 Q.allSettled

var Q = require('q'),
  fs = require('fs');
/**
 *读取文件内容
 *@private
 */
 
function printFileContent(fileName) {
 
  // Todo: 这段代码不够简洁。可以使用Q.denodeify来简化
  var defer = Q.defer();
 
  fs.readFile(fileName, 'utf8', function (err, data) {
    if (!err && data) {
      console.log(data);
      defer.resolve(fileName   ' success ');
    } else {
      defer.reject(fileName   ' fail ');
    }
  })
 
  return defer.promise;
   
}
 
Q.allSettled([printFileContent('nosuchfile.txt'), printFileContent('sample02.txt'), printFileContent('sample03.txt'), printFileContent('sample04.txt')])
  .then(function (results) {
    results.forEach(
      function (result) {
        console.log(result.state);
      }
    );
  });

结束 promise 链

通常,对于一个 promise 链,有两种结束的方式。第一种方式是返回最后一个 promise

如 return foo().then(bar);

第二种方式就是通过 done 来结束 promise 链

如 foo().then(bar).done()

为什么需要通过 done 来结束一个 promise 链呢? 如果在我们的链中有错误没有被处理,那么在一个正确结束的 promise 链中,这个没被处理的错误会通过异常抛出

var Q = require('q'); 
function getPromise(msg, timeout, opt) { 
  var defer = Q.defer(); 
  setTimeout(function () {
    console.log(msg);
    if (opt)
      defer.reject(msg);
    else
      defer.resolve(msg);
  }, timeout); 
  return defer.promise; 
}
 
/**
 * 没有用 done() 结束的 promise 链
 * 由于 getPromse('2',2000,'opt') 返回 rejected, getPromise('3',1000) 就没有执行
 * 然后这个异常并没有任何提醒,是一个潜在的 bug
 */
getPromise('1', 3000)
  .then(function () { return getPromise('2', 2000, 'opt') })
  .then(function () { return getPromise('3', 1000) });
 
/**
 * 用 done() 结束的 promise 链
 * 有异常抛出
 */
getPromise('1', 3000)
  .then(function () { return getPromise('2', 2000, 'opt') })
  .then(function () { return getPromise('3', 1000) })
  .done();

附录:一个 Promise 简单的应用(Node.js笔记(5)promise)

附:Promises/A 规范

promise 代表一个异步操作的最终结果。主要通过 promise 的 then 方法订阅其最终结果的处理回调函数,和订阅因某原因无法成功获取最终结果的处理回调函数。

更对详细见:Promises/A

A 与 A 的不同点

  1. A 规范通过术语 thenable 来区分 promise 对象
  2. A 定义 onFulfilled/onRejectd 必须是作为函数来调用,而且调用过程必须是异步的
  3. A 严格定义了 then 方法链式调用时,onFulfilled/onRejectd 的调用顺序

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持Devmax。

node使用promise替代回调函数的更多相关文章

  1. 利用Node实现HTML5离线存储的方法

    这篇文章主要介绍了利用Node实现HTML5离线存储的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  2. ios – 使用带有NodeJs HTTPS的certificates.cer

    我为IOS推送通知生成了一个.cer文件,我希望将它与NodeJSHTTPS模块一起使用.我发现HTTPS模块的唯一例子是使用.pem和.sfx文件,而不是.cer:有解决方案吗解决方法.cer文件可以使用两种不同的格式进行编码:PEM和DER.如果您的文件使用PEM格式编码,您可以像使用任何其他.pem文件一样使用它(有关详细信息,请参见Node.jsdocumentation):如果您的文件使

  3. 如何在XCode IDE中构建NodeJS?

    如何在XCodeIDE中将NodeJS构建为项目?NodeJS构建指令说它应该用以下内容构建:但是我希望在XCodeIDE中构建.我真正想要做的是在我的应用程序中嵌入NodeJS,所以我想如果我可以在XCode中构建NodeJS,那么我可以调整它以在我建立和运行NodeJS后添加我的应用程序.我想通过让V8在XCode中编译来取得一些进展,现在我正在尝试将NodeJS添加到V8项目中.解决方法在节点存储库根目录中运行./configure–xcode,您将获得所需的node.xcodeproj文件.

  4. 深入云存储系统Swift核心组件:Ring实现原理剖析

    它的目的是用于托管Rackspace的CloudFilesservice,原始项目代号是swift,所以沿用至今。Ring是Swift中最重要的组件,用于记录存储对象与物理位置间映射关系。先来看一下Swift文档中关于Ring的描述:Ring用来确定数据驻留在集群中的位置。有单独对应于Account数据库、container数据库和单个object的ring。Ring使用zone的概念来保证数据的隔离。每个partition的replica都确保放在了不同的zone中。本文逐步深入探讨了Swift如何通过

  5. Swift开发:创建XML文件,包含节点,属性值

    .append;//3创建第二个节点数据letitem2:Item=Item;for{letnode=Node;node.id=i+1;node.attributes=["ID":"\","Name":"N-\","disp":"1","Appliance":"1","Icon":"ic_switch_4"]item2.addNode;}xml.items?

  6. 泛型 – 符合Swift中Comparable的泛型类

    我正在尝试创建一个符合Comparable协议的简单通用节点类,以便我可以轻松地比较节点而无需访问其密钥.当我试图写

  7. swift3 – 将SceneKit对象放在SCNCamera当前方向的前面

    >生成SCNVector4,它定向节点,使其“面向”相机?但是让我有点失落.我看到了许多类似的问题,比如thisone,但没有答案.嘿,如果要将对象放在相对于另一个节点的某个位置,并且与参考节点的方向相同,则可以使用这个更简单的函数:如果您想将’node’2m放在某个’cameraNode’前面,你可以这样称呼:

  8. 如何在Swift中继承NSOperation以将SKAction对象排队以进行串行执行?

    Rob为子类化NSOperation提供了agreatObjective-Csolution,以实现SKAction对象的串行排队机制.我在自己的Swift项目中成功实现了这一点.要使用Actionoperation,请在客户端类中实例化NSOperationQueue类成员:在init方法中添加以下重要行:然后当您准备好向其添加SKActions时,它们会连续运行:您是否需要在任何时候终止操作:希望有所帮助!

  9. 核心数据 – 如何在Swift中定义CoreData关系?

    在CoreData中,我已经从Node到Tag定义了一个无序的多对多关系.我创建了一个这样的Swift实体:现在我想添加一个Tag到Node的一个实例,像这样:但是,这会失败,并显示以下错误:Terminatingappduetouncaughtexception‘NSinvalidargumentexception’,reason:‘Unacceptabletypeofvalueforto-ma

  10. 将“nil”值赋给Swift中的一般类型变量

    您需要将变量声明为可选项:不幸的是,这似乎触发了一个未实现的编译器功能:您可以通过使用NSObject的类型约束声明T来解决它:

随机推荐

  1. Error: Cannot find module ‘node:util‘问题解决

    控制台 安装 Vue-Cli 最后一步出现 Error: Cannot find module 'node:util' 问题解决方案1.问题C:\Windows\System32>cnpm install -g @vue/cli@4.0.3internal/modules/cjs/loader.js:638 throw err; &nbs

  2. yarn的安装和使用(全网最详细)

    一、yarn的简介:Yarn是facebook发布的一款取代npm的包管理工具。二、yarn的特点:速度超快。Yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率,因此安装速度更快。超级安全。在执行代码之前,Yarn 会通过算法校验每个安装包的完整性。超级可靠。使用详细、简洁的锁文件格式和明确的安装算法,Yarn 能够保证在不同系统上无差异的工作。三、y

  3. 前端环境 本机可切换node多版本 问题源头是node使用的高版本

    前言投降投降 重头再来 重装环境 也就分分钟的事 偏要折腾 这下好了1天了 还没折腾出来问题的源头是node 使用的高版本 方案那就用 本机可切换多版本最终问题是因为nodejs的版本太高,导致的node-sass不兼容问题,我的node是v16.14.0的版本,项目中用了"node-sass": "^4.7.2"版本,无法匹配当前的node版本根据文章的提

  4. nodejs模块学习之connect解析

    这篇文章主要介绍了nodejs模块学习之connect解析,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  5. nodejs npm package.json中文文档

    这篇文章主要介绍了nodejs npm package.json中文文档,本文档中描述的很多行为都受npm-config(7)的影响,需要的朋友可以参考下

  6. 详解koa2学习中使用 async 、await、promise解决异步的问题

    这篇文章主要介绍了详解koa2学习中使用 async 、await、promise解决异步的问题,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  7. Node.js编写爬虫的基本思路及抓取百度图片的实例分享

    这篇文章主要介绍了Node.js编写爬虫的基本思路及抓取百度图片的实例分享,其中作者提到了需要特别注意GBK转码的转码问题,需要的朋友可以参考下

  8. CentOS 8.2服务器上安装最新版Node.js的方法

    这篇文章主要介绍了CentOS 8.2服务器上安装最新版Node.js的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  9. node.js三个步骤实现一个服务器及Express包使用

    这篇文章主要介绍了node.js三个步骤实现一个服务器及Express包使用,文章通过新建一个文件展开全文内容,具有一定的参考价值,需要的小伙伴可以参考一下

  10. node下使用UglifyJS压缩合并JS文件的方法

    下面小编就为大家分享一篇node下使用UglifyJS压缩合并JS文件的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

返回
顶部